Backend

Directus

Explain the fetcher mapping and auth integration in @ginjou/with-directus.

@ginjou/with-directus provides two adapters for the Directus SDK.

It gives you a Ginjou fetcher and a Ginjou auth provider. Most apps pass the same Directus client to both.

This package does not change how higher-level Ginjou hooks are used. It only connects them to Directus.

Installation

Install @directus/sdk together with the adapter.

pnpm add @ginjou/with-directus @directus/sdk
PackageSupported version
@directus/sdk^15.0.0

Fetcher

Use createFetcher() with a Directus client that has REST support.

PropRequiredMeaning
clientYesA Directus client with REST capabilities.

Register it through defineFetchersContext().

import { authentication, createDirectus, rest } from '@directus/sdk'
import { defineFetchersContext } from '@ginjou/vue'
import { createFetcher } from '@ginjou/with-directus'

const directus = createDirectus('https://your-directus.example.com')
    .with(rest())
    .with(authentication())

defineFetchersContext({
    default: createFetcher({ client: directus }),
})

The adapter implements getList, getOne, createOne, updateOne, deleteOne, and custom.

Collections and System Collections

Normal resources use the generic SDK item helpers such as readItems() and updateItem().

System resources are different. If the resource starts with directus_ or directus/, the adapter switches to the matching dedicated SDK helper.

Resource nameDirectus SDK helper pattern
postsreadItems, readItem, createItem, updateItem, deleteItem
directus_usersreadUsers, readUser, createUser, updateUser, deleteUser

That lets one resource name work for both normal collections and Directus system collections.

Pagination

List pagination is translated into Directus page and limit.

Ginjou inputDirectus query
pagination.currentpage
pagination.perPagelimit

For totals, the adapter runs a second aggregate() request.

By default it asks Directus for countDistinct: 'id'.

Filters

The adapter converts Ginjou filters into Directus filter objects.

Ginjou operatorDirectus operator
eq_eq
ne_neq
lt_lt
gt_gt
lte_lte
gte_gte
in_in
nin_nin
contains_contains
containss_icontains
ncontains_ncontains
null_null
nnull_nnull
between_between
nbetween_nbetween
startswith_starts_with
nstartswith_nstarts_with
endswith_ends_with
nendswith_nends_with
or_or
and_and

Some case-insensitive negative variants are not currently mapped.

Ginjou operatorBehavior
ncontainssIgnored by the adapter
startswithsIgnored by the adapter
nstartswithsIgnored by the adapter
endswithsIgnored by the adapter
nendswithsIgnored by the adapter

There are two extra rules worth knowing.

CaseBehavior
Filter on searchRouted into Directus search.
getList() default statusExcludes status: archived unless you override it.

If that default status filter is not what you want, override it through meta.query.filter.status.

Sorters

Sorters are converted into Directus sort strings.

Ginjou sorterDirectus sort entry
{ field: 'title', order: 'asc' }title
{ field: 'createdAt', order: 'desc' }-createdAt

Multiple sorters are joined with commas.

For example, title asc plus createdAt desc becomes title,-createdAt.

Meta

The verified meta entry points are meta.query and meta.aggregate.

Meta fieldUsed byWhat it does
meta.querygetList, getOne, createOne, updateOnePass through Directus query options such as fields, filter, sort, page, and limit.
meta.aggregategetListOverride the aggregate descriptor used for total count.

groupBy exists in the current type, but it is not used by the implementation.

Do not rely on it as a working feature yet.

import { useGetList } from '@ginjou/vue'

useGetList({
    resource: 'posts',
    meta: {
        query: {
            fields: ['id', 'title', 'user_created.first_name'],
            filter: {
                status: {
                    _eq: 'published',
                },
            },
        },
    },
})
import { useGetList } from '@ginjou/vue'

useGetList({
    resource: 'posts',
    meta: {
        aggregate: {
            count: '*',
        },
    },
})

Auth

Use createAuth() with a Directus client that has authentication and REST support.

Most apps reuse the same Directus client they already passed to createFetcher().

Register it through defineAuthContext().

import { authentication, createDirectus, rest } from '@directus/sdk'
import { defineAuthContext } from '@ginjou/vue'
import { createAuth } from '@ginjou/with-directus'

const directus = createDirectus('https://your-directus.example.com')
    .with(rest())
    .with(authentication())

defineAuthContext(createAuth({ client: directus }))

Login

login() maps to client.login().

The adapter currently supports two verified login types.

Login typeInputDirectus call
passwordemail, password, optional optionsclient.login(email, password, options)
ssoprovider, optional optionsclient.login('placeholder', 'placeholder', { provider, ...options })

Password login:

import { useLogin } from '@ginjou/vue'

const { mutateAsync: login } = useLogin()

await login({
    type: 'password',
    params: {
        email: 'user@example.com',
        password: 'password123',
    },
})

SSO login:

import { useLogin } from '@ginjou/vue'

const { mutateAsync: login } = useLogin()

await login({
    type: 'sso',
    params: {
        provider: 'google',
    },
})

Logout

logout() maps to client.logout().

It clears the Directus session through the SDK.

Identity

getIdentity() maps to client.request(readMe()).

That returns the current Directus user profile.

Check Authentication

check() maps to client.getToken().

Token stateResult
Token exists{ authenticated: true }
No token{ authenticated: false }

Check Error

checkError() inspects Directus client errors.

It returns { logout: true } only for verified auth-related Directus error codes.

Directus error codeBehavior
TOKEN_EXPIREDReturn { logout: true, error }
INVALID_CREDENTIALSReturn { logout: true, error }
INVALID_IPReturn { logout: true, error }
INVALID_OTPReturn { logout: true, error }

Other errors return an empty object, so non-auth failures do not force logout automatically.

Copyright © 2026