Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Df Core Laravel Package

dreamfactory/df-core

Core foundation for DreamFactory's Laravel-based API platform, providing shared contracts, utilities, and service infrastructure that power modules for exposing, securing, and managing REST APIs.

View on GitHub
Deep Wiki
Context7

Getting Started

dreamfactory/df-core is not a general-purpose Laravel utility — it is the shared foundation of the DreamFactory API platform (the service abstraction, contracts, base models, providers, and utilities that df-system, df-sqlite, connector packages, and the API docs package all build on). That framing determines how you adopt it.

Choose your entry point first:

  • You want to run DreamFactory → install dreamfactory/df (the platform). df-core arrives transitively; you never touch it directly.
  • You want to extend DreamFactory → this is your package. You are building a custom service type (a connector to an internal system, a bespoke REST surface, a scripting host) and df-core supplies the base classes and contracts you extend.

Minimal install (extension author):

composer require dreamfactory/df-core

Pin the constraint to the same major/minor line as the platform you are targeting. df-core is released in lockstep with dreamfactory/df; a floating constraint will eventually pull contracts that the host platform does not implement. As of 1.0.18 the aligned platform release is DreamFactory 7.7.1 — if you are on 7.7.0 or earlier, read the 1.0.18 upgrade notes in Gotchas before bumping, because several defaults changed under you.

Read these first, in order:

  1. src/Contracts/ — ServiceInterface, ServiceTypeInterface, cache and script contracts. These define what the platform will call on your code.
  2. src/Services/BaseService.php and BaseRestService.php — the lifecycle you inherit: request handling, verb dispatch, event firing, and API doc assembly.
  3. src/Providers/ — how service types, routes, and config get registered into the container.
  4. src/Models/Service.php — service instances are database rows, not just config entries; this is the single biggest conceptual adjustment for Laravel developers.
  5. config/df.php — the platform's config surface, including the serviceTypes map.
  6. The curl/HTTP wrapper utility — since 1.0.18 it enforces SSRF and TLS defaults you need to know about before writing your first outbound call.

First use case — a custom service type. Extend the base REST service, implement the verbs you support, and return a documented API surface:

namespace App\Df\Services;

use DreamFactory\Core\Services\BaseRestService;

class InventoryService extends BaseRestService
{
    const SERVICE_TYPE = 'inventory';

    protected function handleGET($resource, array $params = [])
    {
        return $this->buildResponse(['sku' => $resource]);
    }

    protected function handlePOST($resource, array $payload = [], array $params = [])
    {
        // ...
    }

    public function getApiDoc()
    {
        // return a Swagger 2.0 fragment for this service
    }
}

Register the type (provider + serviceTypes config), then create an instance through the admin UI or the /api/v2/system/service endpoint. That last step trips people up: the class being registered does not mean an API exists — an instance row must be created and given a role/API key before it responds.

Implementation Patterns

Verb methods, not routes. You do not define Laravel routes. BaseRestService dispatches to handleGET/handlePOST/handlePUT/handlePATCH/handleDELETE/handleMERGE based on the HTTP method, with $resource being the remainder of the path after the service name. This gives you a consistent REST grammar across every connector in the platform and is the main reason to build on df-core rather than a plain controller — your service shows up in the same API docs, role system, and scripting layer as everything else.

Always return the platform response object. Use ServiceResponse (or $this->buildResponse(...)) rather than returning arrays. The platform needs content, content_type, and status_code to apply content negotiation, event hooks, and response caching. Returning a bare array typically produces a 200 with a mangled body and no useful error trail. Since 1.0.18, validation failures raised during an update are reported to the client as 400 rather than the old 500; if you surface validation errors yourself, mirror that convention so clients see one consistent contract.

Use the platform's HTTP wrapper for outbound calls, and know its defaults. Connectors should not reach for raw Guzzle. The df-core curl wrapper is the SSRF-hardened path, and 1.0.18 tightened it:

  • Redirects are not followed automatically. If an upstream API legitimately redirects, you must opt in, and each opted-in hop is validated and pinned to its resolved IP address.
  • SSL peer and host verification are on by default. Self-signed or internal-CA upstreams need an explicit, documented exception rather than a global verify => false.
  • Private/link-local address space is blocked by design.

If an existing connector of yours relied on follow-redirects or skipped verification, that is upgrade work, not a bug report.

Filters: write them the way you mean them. The database filter parser was repaired in 1.0.18. Before, unparenthesized a='x' OR b='y' silently matched nothing; now it parses correctly. BETWEEN / NOT BETWEEN are implemented (expanded to range comparisons) rather than being silently unsupported. Two consequences for your code: any filter string you previously "worked around" with extra parentheses or >= / <= pairs can be simplified, and any filter that was quietly returning an empty set may now return rows. Re-run your filter test fixtures after upgrading.

Keep getApiDoc() in sync with behavior. The Swagger fragment drives the generated /api/v2/api_docs output, the admin UI's interactive docs, and (in some deployments) request/response validation. Treat it as code: if you add a query parameter in handleGET, add it to the doc in the same commit. Mismatches are silent until a consumer generates a broken SDK.

Know what your roles can be granted. The service access list is the vocabulary your role definitions are written against. _spec is now listed as a grantable operation (previously it existed but could not be assigned to a role), so if you expose a spec endpoint, document it as a permission rather than assuming it inherits the table/verb grants.

Consume services internally through the service manager. If you need to call another DreamFactory service from your own (fan-out/aggregation), resolve it via the service manager rather than issuing HTTP requests back to yourself. You inherit the current user's session, role restrictions, and event context, and you avoid a pointless network hop:

$svc = $this->serviceManager->getService('mysql', $request->getApiKey());
$result = $svc->handleRequest($request, 'GET', 'customers', $params);

Layer caching per service instance, not globally. df-core exposes cache contracts and traits used by connectors. Namespace keys by service id plus resource path plus a hash of params — DreamFactory is multi-tenant and multi-role by design, and a cache key that ignores the role or API key will leak data across tenants. Cache the expensive upstream call, not the formatted response, when role-based field filtering applies.

Treat the event system as your AOP layer. Service lifecycle events fire around request handling. Cross-cutting concerns — audit logging, per-tenant rate limiting, response scrubbing, timing metrics — belong in listeners registered from your own service provider, not duplicated into every connector:

Event::listen('df.service.pre_process', function ($event) { /* ... */ });

Custom scripting engines. If your use case is "let customers write small transformations," implement the script engine contract rather than inventing a mini-language layered on top of the service. The platform's scripting API already wires your service's data into the script context and back.

If you ship MCP query templates, use the platform's argument shape. 1.0.18 changed template arguments to snake_case and moved group counts onto aggregate_data. Templates authored against 1.0.17 will render with empty or undefined arguments after the upgrade — treat template definitions as part of your service's public surface and version them alongside the package.

Package it as a Composer package. Custom service types belong in a package with its own service provider, not in app/. The platform's extension model expects to find providers and asset/config publishing in a package, which also makes version pinning and rollout across environments tractable.

Gotchas and Tips

  • It is not a standalone package. df-core assumes the platform's database schema, migrations, config keys, and other df-* packages. Pulling it into an ordinary Laravel app gives you base classes that reference tables and config that do not exist. If you are not inside a DreamFactory installation, this package is the wrong dependency.
  • Version lockstep is mandatory. df-core and the platform share contracts that change between releases. Match versions exactly, and treat any platform upgrade as requiring a compatibility run of your custom services — the failures often surface at runtime in handleRequest, not at composer update. 1.0.18 pairs with DreamFactory 7.7.1.
  • 1.0.18 is a behavior-change release dressed as a bug-fix release. Nothing is formally deprecated, but several defaults flipped. Before bumping, audit:
    • Outbound HTTP that relied on automatic redirect-following.
    • Outbound HTTP to endpoints with self-signed or internal-CA certificates (verification is now enforced).
    • Anything that reads the service-type catalog from the API root anonymously — that listing now requires authentication, so scripts and dashboards that scraped it will start getting 401s.
    • Client error handling that matched on 500 for update validation failures; those are now 400.
    • Filter strings that were written around the old parser's limitations (see below).
  • Filter fixes can widen previously-empty result sets. a='x' OR b='y' used to match nothing; it now matches correctly. If downstream code "worked" because that filter returned zero rows, it will now process real data — and possibly more than the role was expected to see. Re-run your role/permission tests against real datasets, not fixtures.
  • Package import now applies role access it used to skip. 1.0.18 resolves imported service ids without the cached id/name map, so role service-access rows for services created in the same import are no longer dropped. After a re-import, roles may grant access that the previous bug silently withheld — verify permissions explicitly rather than assuming import output is unchanged.
  • Service type registration is two-and-a-half steps. The provider must be registered (via Composer discovery or the platform's package/provider manifest), the type must appear in the serviceTypes configuration, and an instance must exist as a database row. Missing any one produces the same unhelpful symptom: the type is invisible in the admin UI.
  • Beware double provider registration. Composer auto-discovery plus the platform's own package manifest can register your provider twice. Symptoms range from duplicated event listeners to odd container resolution. Check php artisan about / the loaded providers list when behavior seems doubled.
  • A broken getApiDoc() breaks more than your service. The API docs endpoint aggregates all services; one malformed Swagger fragment can take down the entire docs page and, in stricter setups, fail API generation for unrelated services. Guard against unset config and validate the fragment shape.
  • Permission checks are not automatic. Inheriting BaseRestService gives you dispatch, not authorization. Role and API-key restrictions are applied by the platform around the standard service flow, but any custom resource paths or admin-only verbs you add need explicit checks. Assume a request that reaches your handler has already passed platform-level checks — and only those.
  • Response format surprises. DreamFactory serializes and may wrap or transform responses based on content_type and platform config. If clients report "the JSON has an extra envelope," check the response object and config before rewriting your handler. ?model=true metadata is now built from the caller's actual _table listing, so model responses are more accurate but may differ in shape from what a client cached.
  • Null descriptions are now legal. A service with no description no longer blows up; do not write code that assumes description is always a non-empty string.
  • Debugging: enable APP_DEBUG in the environment, watch the platform's log channel, and use the platform's artisan commands (service listing, config inspection) before adding dd(). Custom service exceptions are caught and converted by the platform, so raw stack traces rarely reach the client — log the exception and include a stable error code/message pair in your response so support can correlate. For outbound HTTP failures post-1.0.18, log the resolved IP, whether the redirect was opted in, and the TLS verification outcome; "connection failed" is otherwise indistinguishable between an SSRF block and a bad certificate.
  • Validate the SSRF hardening against your own topology. If your connectors legitimately talk to services on private subnets or via internal hostnames that resolve privately, confirm the new address checks permit them in your deployment before rollout. This class of failure only appears in the environment where the connector actually runs.
  • Low community surface. With ~16 stars and no dependents, there is essentially no third-party Q&A. Your real documentation is the source (src/Contracts and BaseRestService are short and worth reading end to end), the dreamfactory/df repository for working examples of every service type, the release PRs on GitHub (1.0.18's six fix PRs document real-world failure modes better than any guide), and the DreamFactory docs site. Budget time for reading reference implementations of connectors — that is faster than searching.
  • Testing custom services: the platform's base test cases expect a bootstrapped DreamFactory application. Structure your service so the verb methods are callable against an injected request/parameters object, letting you unit-test logic without a full platform boot, and reserve full-stack tests for registration and API-doc generation. Add explicit fixtures for filter parsing (bare AND/OR, BETWEEN) and for role service-access after package import — both are areas where 1.0.18 changed observable behavior.
  • Licensing: the package is Apache-2.0, but some platform features your service may want to integrate with are gated by edition. Confirm feature availability in the deployment you target before designing around an enterprise-only capability.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect
youbar/easy-crud
c-point-labs/laravel-hostyourai
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo