@molecule/api-resource
Infrastructure · resource · API (Node) · v1.0.5 · Apache-2.0
CRUD resource framework
npm install @molecule/api-resourceHow it works
@molecule/api-resource is a infrastructure package for the API (Node) side (resource).
import { z } from 'zod'
import {
createRequestHandler,
create,
read,
update,
del,
query,
type MoleculeRequest,
} from '@molecule/api-resource'
import { types } from '@molecule/api-resource'
import { basePropsSchema } from '@molecule/api-resource/schema'
// Every resource schema extends basePropsSchema (id/createdAt/updatedAt).
const userSchema = basePropsSchema.extend({ name: z.string(), email: z.string() })
type UserProps = z.infer<typeof userSchema>
// Each CRUD factory takes the resource descriptor and returns an async
// operation, e.g. ({ props }) or ({ id }) => { statusCode, body }
// One descriptor, typed as Resource, drives every CRUD factory:
const userResource: types.Resource = { name: 'User', tableName: 'users', schema: userSchema }
const createUser = create<UserProps>(userResource)
const updateUser = update<UserProps>(userResource)
// read/del type their descriptor with the concrete props — cast the shared one:
const readUser = read(userResource as types.Resource<UserProps>)
const deleteUser = del(userResource as types.Resource<UserProps>)
// Operations do NOT take (req, res) directly — adapt each one by mapping
// request fields (req.body, req.params.id) to its arguments, then wrap
// with createRequestHandler for Express
const requestHandlerMap = {
create: createRequestHandler(
async (req: MoleculeRequest) => await createUser({ props: req.body }),
),
read: createRequestHandler(async (req: MoleculeRequest) => await readUser({ id: req.params.id })),
// ...
}Works with: @molecule/api-bond, @molecule/api-database, @molecule/api-i18n, @molecule/api-locales-resource, @molecule/api-utilities-validation
Reference
Base resource patterns for molecule.dev.
Provides CRUD operation factories and request handler utilities for
building RESTful API resources. Every @molecule/api-resource-* package
(user, project, device, ...) is built on these factories.
A handler returns { statusCode, headers?, body } and NEVER writes to res — not through
res.json, not through res.send, not through res.end. createRequestHandler is the wrapper
that applies the returned object (res.set(headers), res.status(statusCode), res.send(body)),
and a handler returns null only when the request should fall through to next(). Calling a
handler directly from your own route is therefore only correct if you apply that return value
yourself:
const { statusCode, headers, body } = await handler(req, res)
if (headers) res.set(headers)
res.status(statusCode).send(body)
Monkey-patching res.json (or res.send/res.end) to re-shape a handler's answer intercepts
nothing the handler produces, so the request it was meant to answer is left unanswered and hangs.
Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit
src/index.tsJSDoc, not this file.
Quick Start
import { z } from 'zod'
import {
createRequestHandler,
create,
read,
update,
del,
query,
type MoleculeRequest,
} from '@molecule/api-resource'
import { types } from '@molecule/api-resource'
import { basePropsSchema } from '@molecule/api-resource/schema'
// Every resource schema extends basePropsSchema (id/createdAt/updatedAt).
const userSchema = basePropsSchema.extend({ name: z.string(), email: z.string() })
type UserProps = z.infer<typeof userSchema>
// Each CRUD factory takes the resource descriptor and returns an async
// operation, e.g. ({ props }) or ({ id }) => { statusCode, body }
// One descriptor, typed as Resource, drives every CRUD factory:
const userResource: types.Resource = { name: 'User', tableName: 'users', schema: userSchema }
const createUser = create<UserProps>(userResource)
const updateUser = update<UserProps>(userResource)
// read/del type their descriptor with the concrete props — cast the shared one:
const readUser = read(userResource as types.Resource<UserProps>)
const deleteUser = del(userResource as types.Resource<UserProps>)
// Operations do NOT take (req, res) directly — adapt each one by mapping
// request fields (req.body, req.params.id) to its arguments, then wrap
// with createRequestHandler for Express
const requestHandlerMap = {
create: createRequestHandler(
async (req: MoleculeRequest) => await createUser({ props: req.body }),
),
read: createRequestHandler(async (req: MoleculeRequest) => await readUser({ id: req.params.id })),
// ...
}
Type
infrastructure
Installation
npm install @molecule/api-resource @molecule/api-bond @molecule/api-database @molecule/api-i18n @molecule/api-locales-resource @molecule/api-utilities-validation uuid zod
API
Interfaces
Response
Structured response object returned by resource handlers. Contains the HTTP status code, optional headers, and the response body payload.
interface Response {
/**
* HTTP status code to return.
*/
statusCode: number
/**
* Optional map of HTTP header keys to values.
*/
headers?: Record<string, string>
/**
* The response payload to return.
*/
body: any
}
MoleculeRequest
Framework-agnostic HTTP request interface. Structurally compatible with Express's
Request — Express objects satisfy this interface without adapters, but the type
doesn't depend on Express, allowing use with Fastify, Hono, or Lambda.
params is Record<string, any> on purpose. Express 5 declares
ParamsDictionary as { [key: string]: string | string[] } — a wildcard or a
repeated parameter yields an array — so Record<string, string> is NOT a
supertype of it, and every Express handler passing its own Request to a
function declared on this interface (authorization.set(req, …)) failed with
TS2345 and was told to cast. This is the same shape body and query already
use. A reader that needs one segment as a string checks it
(typeof req.params.id === 'string') rather than trusting the annotation;
the packages that own the routes do exactly that where it matters (see
@molecule/api-bonds-default-express's getParamId).
interface MoleculeRequest {
body: any
params: Record<string, any>
query: Record<string, any>
headers: Record<string, string | string[] | undefined>
cookies?: Record<string, string>
/**
* Client IP address. Populated by Express (honoring `trust proxy`) and other
* frameworks; optional because it isn't guaranteed in every adapter. Used for
* IP-keyed rate limiting on auth endpoints.
*/
ip?: string
on?(event: string, listener: (...args: unknown[]) => void): void
}
MoleculeResponse
Framework-agnostic HTTP response interface. Structurally compatible with Express's
Response — includes status(), json(), send(), set(), cookie methods,
streaming (write), and locals for per-request data sharing.
interface MoleculeResponse {
status(code: number): this
json(body: unknown): void
send(body: unknown): void
end(): void
set(headers: Record<string, string>): void
setHeader(name: string, value: string): void
cookie(name: string, value: string, options?: Record<string, unknown>): void
write(chunk: string | Buffer): boolean
flushHeaders?(): void
locals: Record<string, any>
}
Query
The available resource query options.
interface Query {
/**
* The maximum number of query results.
*/
limit?: number
/**
* An indexed property to order the results by.
*/
orderBy?: `createdAt` | `updatedAt`
/**
* The direction to order the results.
*/
orderDirection?: `asc` | `desc`
/**
* Constrains the results to values less than these properties, when provided.
*/
before?: {
/**
* Constrains the results to resources created before the provided timestamp.
*
* Usually an ISO 8601 timestamp.
*/
createdAt?: string
/**
* Constrains the results to resources updated before the provided timestamp.
*
* Usually an ISO 8601 timestamp.
*/
updatedAt?: string
}
/**
* Constrains the results to values greater than these properties, when provided.
*/
after?: {
/**
* Constrains the results to resources created after the provided timestamp.
*
* Usually an ISO 8601 timestamp.
*/
createdAt?: string
/**
* Constrains the results to resources updated after the provided timestamp.
*
* Usually an ISO 8601 timestamp.
*/
updatedAt?: string
}
}
ErrorFallback
Fallback HTTP response used when a caught error is NOT a molecule-tagged error (i.e. a genuinely-unexpected failure).
interface ErrorFallback {
/** HTTP status for the fallback (typically 500). */
status: number
/** Human-readable fallback message (already localized by the caller). */
message: string
/** Machine-readable key the frontend maps to a localized message. */
errorKey: string
}
Types
Handler
A resource handler function that processes a request and returns a structured
response object. If it returns null or undefined, the request is passed
to the next middleware via next().
type Handler = (
req: MoleculeRequest,
res: MoleculeResponse,
) => Promise<Response | null | undefined> | Response | null | undefined
req— The incoming HTTP request.res— The HTTP response (available but handlers should return a Response instead of callingresdirectly).
MoleculeNextFunction
Framework-agnostic next function. Call with no arguments to pass to the next middleware, or with an error to trigger error-handling middleware.
type MoleculeNextFunction = (err?: unknown) => void
err— Optional error to forward to error-handling middleware.
MoleculeRequestHandler
Framework-agnostic middleware function signature matching Express's
(req, res, next) => void pattern.
type MoleculeRequestHandler = (
req: MoleculeRequest,
res: MoleculeResponse,
next: MoleculeNextFunction,
) => void | Promise<void>
Functions
create(resource)
Creates a handler to create a resource.
Uses the bonded DataStore from @molecule/api-database for database-agnostic operations.
Example usage:
import { create as resourceCreate } from '@molecule/api-resource'
import type * as types from '../../types.js'
export const create = ({ name, tableName, schema }: types.Resource) => {
const create = resourceCreate({ name, tableName, schema })
return async (req: MoleculeRequest) => {
const props = req.body
return await create({ props })
}
}
function create({
name,
tableName,
schema,
}: types.Resource<unknown>): ({
props: createProps,
id,
}: {
props: CreateProps
id?: CreatedProps['id']
}) => Promise<
| { statusCode: number; body: { error: string; errorKey: string; props?: undefined } }
| { statusCode: number; body: { props: CreatedProps; error?: undefined; errorKey?: undefined } }
>
resource— The resource descriptor.resource.name— Human-readable resource name used in error messages (e.g.'User').resource.tableName— Database table to insert the new row into.resource.schema— Zod schema to validate the incoming props against.
Returns: A curried async function that accepts { props, id? } and returns a { statusCode, body } response.
createRequestHandler(handler)
Wraps a resource handler into Express-compatible middleware. Calls the handler,
then applies statusCode, headers, and body to the response. If the handler
returns null/undefined, calls next(). Catches errors and forwards them as
i18n-translated error messages.
function createRequestHandler(
handler: Handler,
): (req: MoleculeRequest, res: MoleculeResponse, next: MoleculeNextFunction) => Promise<void>
handler— The resource handler function to wrap.
Returns: An Express-compatible middleware function (req, res, next) => void.
del(resource)
Creates a handler to delete a resource by its id.
Note: We use
deleverywhere instead ofdeletesincedeleteis a reserved keyword in JavaScript.
Uses the bonded DataStore from @molecule/api-database for database-agnostic operations.
Example usage:
import { del as resourceDel } from '@molecule/api-resource'
import type * as types from '../../types.js'
export const del = ({ name, tableName, schema }: types.Resource) => {
const del = resourceDel({ name, tableName, schema })
return async (req: MoleculeRequest) => {
const id = req.params.id
return await del({ id })
}
}
function del({ name, tableName }: DeletedResource): ({ id }: { id: Props['id'] }) => Promise<
| { statusCode: number; body: { error: string; errorKey: string; props?: undefined } }
| {
statusCode: number
body: { props: { id: Props['id'] }; error?: undefined; errorKey?: undefined }
}
>
resource— The resource descriptor.resource.name— Human-readable resource name used in error messages (e.g.'User').resource.tableName— Database table to delete the row from.
Returns: A curried async function that accepts { id } and returns a { statusCode, body } response.
query(resource)
Creates a request handler to be used to query resources.
Uses the bonded DataStore from @molecule/api-database for database-agnostic operations.
function query({
tableName,
}: QueriedResource): (
req: MoleculeRequest,
) => Promise<
| { statusCode: number; body: { error: string; errorKey: string } }
| { statusCode: number; body: Props[] }
>
resource— The resource descriptor.resource.tableName— Database table to query resources from.
Returns: A curried async handler that accepts a MoleculeRequest (with query params for pagination/sorting) and returns a { statusCode, body } response.
read(resource)
Creates a handler to read a resource by its id.
Uses the bonded DataStore from @molecule/api-database for database-agnostic operations.
Example usage:
import { read as resourceRead } from '@molecule/api-resource'
import type * as types from '../../types.js'
export const read = ({ name, tableName, schema }: types.Resource) => {
const read = resourceRead({ name, tableName, schema })
return async (req: MoleculeRequest) => {
const id = req.params.id
return await read({ id })
}
}
function read({
tableName,
}: ReadResource): ({
id,
props,
}: {
id: ReadProps['id']
props?: ReadProps
}) => Promise<
| { statusCode: number; body: { props: ReadProps; error?: undefined; errorKey?: undefined } }
| { statusCode: number; body: { error: string; errorKey: string; props?: undefined } }
>
resource— The resource descriptor.resource.tableName— Database table to query the resource from.
Returns: A curried async function that accepts { id, props? } and returns a { statusCode, body } response.
respondError(res, error, fallback)
Respond to a caught handler error, honoring molecule's "tagged error" convention.
A tagged error carries BOTH a numeric statusCode AND a string errorKey — the same
contract @molecule/api-server-default-express's error middleware (classifyTaggedError)
uses. Providers throw these for expected, user-actionable conditions — e.g. a
config-missing throw (503 + 'config.notConfigured') when STRIPE_SECRET_KEY is unset.
Handlers that catch errors MUST funnel through this instead of a blanket
res.status(500), so a tagged error surfaces its REAL status + errorKey rather than
being flattened to a generic 500. Otherwise the app looks broken at the exact moment the
user tries to use the feature — the worst possible time for a payment (the conversion
path). A swallowed tag is why the middleware-only fix was insufficient: ~185 handlers
catch-and-500, so the tag never reached the middleware.
Tagged errors are logged at warn (expected/actionable, not a server fault); everything
else is logged at error and returns the caller's fallback. Requiring BOTH fields is
deliberate: it keeps arbitrary library errors that merely carry a .statusCode (e.g. an
AWS SDK 403) from being surfaced with a status molecule never chose.
function respondError(res: MoleculeResponse, error: unknown, fallback: ErrorFallback): void
res— The response object.error— The caught error.fallback— Status/message/errorKey to use whenerrorisn't a tagged error.
update(resource)
Creates a handler to update a resource by its id and some props.
Uses the bonded DataStore from @molecule/api-database for database-agnostic operations.
Example usage:
import { update as resourceUpdate } from '@molecule/api-resource'
import type * as types from '../../types.js'
export const update = ({ name, tableName, schema }: types.Resource) => {
const update = resourceUpdate({ name, tableName, schema })
return async (req: MoleculeRequest) => {
const id = req.params.id
const props = req.body
return await update({ id, props })
}
}
function update({
name,
tableName,
schema,
}: types.Resource<unknown>): ({
id,
props: updateProps,
}: {
id: UpdatedProps['id'] | types.Props['id']
props: UpdateProps
}) => Promise<
| { statusCode: number; body: { error: string; errorKey: string; props?: undefined } }
| { statusCode: number; body: { props: UpdatedProps; error?: undefined; errorKey?: undefined } }
>
resource— The resource descriptor.resource.name— Human-readable resource name used in error messages (e.g.'User').resource.tableName— Database table to update the row in.resource.schema— Zod schema to validate the incoming props against.
Returns: A curried async function that accepts { id, props } and returns a { statusCode, body } response.
Constants
i18nRegistered
Sentinel value confirming that locale translations for the resource package
have been registered (or attempted). Always true after this module loads.
const i18nRegistered: true
querySchema
The resource query Zod schema.
const querySchema: z.ZodObject<
{
limit: z.ZodDefault<z.ZodCoercedNumber<unknown>>
orderBy: z.ZodDefault<z.ZodEnum<{ createdAt: 'createdAt'; updatedAt: 'updatedAt' }>>
orderDirection: z.ZodDefault<z.ZodEnum<{ asc: 'asc'; desc: 'desc' }>>
before: z.ZodOptional<
z.ZodObject<
{ createdAt: z.ZodOptional<z.ZodString>; updatedAt: z.ZodOptional<z.ZodString> },
z.core.$strip
>
>
after: z.ZodOptional<
z.ZodObject<
{ createdAt: z.ZodOptional<z.ZodString>; updatedAt: z.ZodOptional<z.ZodString> },
z.core.$strip
>
>
},
z.core.$strip
>
Namespaces
schema
Re-exported as a namespace — reach every member below through it (e.g. schema.<name>).
schema.basePropsSchema
Base Zod schema with the three fields every resource row must have:
id (UUID), createdAt (ISO datetime), and updatedAt (ISO datetime).
const basePropsSchema: z.ZodObject<
{ id: z.ZodString; createdAt: z.ZodString; updatedAt: z.ZodString },
z.core.$strip
>
schema.BaseProps
TypeScript type inferred from basePropsSchema, containing id, createdAt, and updatedAt.
type BaseProps = z.infer<typeof basePropsSchema>
schema.responseSchema(propsSchema)
Creates a Zod schema for the standard { statusCode, body } response
returned by resource CRUD operations.
function responseSchema(
propsSchema: T,
): z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>
propsSchema— The Zod schema for the resource's props (e.g.userPropsSchema).
Returns: A Zod schema that validates { statusCode: number, body: { props? } | T[] }.
schema.resourceSchema(_propsSchema)
Creates a Zod schema that validates a resource descriptor object
({ name, tableName, schema }).
function resourceSchema(
_propsSchema: T,
): z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>
_propsSchema— The Zod schema for the resource's props (used for type inference only).
Returns: A Zod schema that validates the resource descriptor shape.
schema.validate(schema, data)
Validates data against a Zod schema, throwing an Error with a
human-readable message listing all validation failures if invalid.
function validate(
schema: z.ZodType<T, unknown, z.core.$ZodTypeInternals<T, unknown>>,
data: unknown,
): T
schema— The Zod schema to validate against.data— The data to validate.
Returns: The parsed and typed data if validation succeeds.
schema.safeParse(schema, data)
Validates data against a Zod schema without throwing. Returns a discriminated
union: { success: true, data } on success or { success: false, error } on failure.
function safeParse(
schema: z.ZodType<T, unknown, z.core.$ZodTypeInternals<T, unknown>>,
data: unknown,
): { success: true; data: T } | { success: false; error: z.ZodError }
schema— The Zod schema to validate against.data— The data to validate.
Returns: A discriminated union indicating success with parsed data, or failure with a ZodError.
schema.partial(schema)
Creates a schema where all fields are optional, suitable for validating partial update payloads where only changed fields are sent.
function partial(
schema: z.ZodObject<T, z.core.$strip>,
): z.ZodObject<{ [K in keyof T]: z.ZodOptional<T[K]> }, z.core.$strip>
schema— The Zod object schema to make partial.
Returns: A new Zod schema with all fields marked optional.
schema.pick(schema, keys)
Creates a schema with only the specified fields, useful for create payloads where only a subset of the full props should be provided by the client.
function pick(
schema: z.ZodObject<T, z.core.$strip>,
keys: readonly K[],
): z.ZodObject<Pick<T, K>, z.core.$strip>
schema— The Zod object schema to pick from.keys— The field names to include in the new schema.
Returns: A new Zod schema containing only the picked fields.
types
Re-exported as a namespace — reach every member below through it (e.g. types.<name>).
types.BaseProps
TypeScript type inferred from basePropsSchema, containing id, createdAt, and updatedAt.
type BaseProps = z.infer<typeof basePropsSchema>
types.Props
The resource's properties to be stored in the database.
Used by CRUD operations for type inference.
interface Props {
/**
* Usually a UUID.
*/
id: string
/**
* When the resource was created.
*
* Usually an ISO 8601 timestamp.
*/
createdAt: string
/**
* When the resource was last updated.
*
* Usually an ISO 8601 timestamp.
*/
updatedAt: string
}
types.Resource
An object describing the resource.
The schema can be a Zod schema (z.ZodTypeAny) for new code, or any other schema type for backward compatibility.
interface Resource<T = unknown> {
/**
* The name of the resource.
*
* @example User
*/
name: string
/**
* The database table name for the resource.
*
* @example users
*/
tableName: string
/**
* The schema used when validating props.
*
* Can be a Zod schema (z.ZodTypeAny) or other schema formats.
*/
schema: T
}
types.ZodResource
An object describing the resource with a Zod schema.
Use this type when you want to enforce Zod schema usage.
interface ZodResource<T extends z.ZodTypeAny = z.ZodTypeAny> {
name: string
tableName: string
schema: T
}
types.Response
Standard response from resource operations.
interface Response<T = unknown> {
/**
* HTTP status code to return.
*/
statusCode: number
/**
* The response payload.
*/
body:
| {
props?: T
error?: string
errorKey?: string
}
| T[]
}
utilities
Re-exported as a namespace — reach every member below through it (e.g. utilities.<name>).
utilities.ZodError
An Error-like class used to store Zod validation issues.
interface ZodError<T = unknown> extends $ZodError<T> {
/** @deprecated Use the `z.treeifyError(err)` function instead. */
format(): core.$ZodFormattedError<T>
format<U>(mapper: (issue: core.$ZodIssue) => U): core.$ZodFormattedError<T, U>
/** @deprecated Use the `z.treeifyError(err)` function instead. */
flatten(): core.$ZodFlattenedError<T>
flatten<U>(mapper: (issue: core.$ZodIssue) => U): core.$ZodFlattenedError<T, U>
/** @deprecated Push directly to `.issues` instead. */
addIssue(issue: core.$ZodIssue): void
/** @deprecated Push directly to `.issues` instead. */
addIssues(issues: core.$ZodIssue[]): void
/** @deprecated Check `err.issues.length === 0` instead. */
isEmpty: boolean
}
utilities.ZodSchema
interface ZodType<
out Output = unknown,
out Input = unknown,
out Internals extends core.$ZodTypeInternals<Output, Input> = core.$ZodTypeInternals<
Output,
Input
>,
> extends core.$ZodType<Output, Input, Internals> {
def: Internals['def']
type: Internals['def']['type']
/** @deprecated Use `.def` instead. */
_def: Internals['def']
/** @deprecated Use `z.output<typeof schema>` instead. */
_output: Internals['output']
/** @deprecated Use `z.input<typeof schema>` instead. */
_input: Internals['input']
'~standard': ZodStandardSchemaWithJSON<this>
/** Converts this schema to a JSON Schema representation. */
toJSONSchema(params?: core.ToJSONSchemaParams): core.ZodStandardJSONSchemaPayload<this>
check(...checks: (core.CheckFn<core.output<this>> | core.$ZodCheck<core.output<this>>)[]): this
with(...checks: (core.CheckFn<core.output<this>> | core.$ZodCheck<core.output<this>>)[]): this
clone(
def?: Internals['def'],
params?: {
parent: boolean
},
): this
register<R extends core.$ZodRegistry>(
registry: R,
...meta: this extends R['_schema']
? undefined extends R['_meta']
? [core.$replace<R['_meta'], this>?]
: [core.$replace<R['_meta'], this>]
: ['Incompatible schema']
): this
brand<T extends PropertyKey = PropertyKey, Dir extends 'in' | 'out' | 'inout' = 'out'>(
value?: T,
): PropertyKey extends T ? this : core.$ZodBranded<this, T, Dir>
parse(data: unknown, params?: core.ParseContext<core.$ZodIssue>): core.output<this>
safeParse(
data: unknown,
params?: core.ParseContext<core.$ZodIssue>,
): parse.ZodSafeParseResult<core.output<this>>
parseAsync(data: unknown, params?: core.ParseContext<core.$ZodIssue>): Promise<core.output<this>>
safeParseAsync(
data: unknown,
params?: core.ParseContext<core.$ZodIssue>,
): Promise<parse.ZodSafeParseResult<core.output<this>>>
spa: (
data: unknown,
params?: core.ParseContext<core.$ZodIssue>,
) => Promise<parse.ZodSafeParseResult<core.output<this>>>
encode(data: core.output<this>, params?: core.ParseContext<core.$ZodIssue>): core.input<this>
decode(data: core.input<this>, params?: core.ParseContext<core.$ZodIssue>): core.output<this>
encodeAsync(
data: core.output<this>,
params?: core.ParseContext<core.$ZodIssue>,
): Promise<core.input<this>>
decodeAsync(
data: core.input<this>,
params?: core.ParseContext<core.$ZodIssue>,
): Promise<core.output<this>>
safeEncode(
data: core.output<this>,
params?: core.ParseContext<core.$ZodIssue>,
): parse.ZodSafeParseResult<core.input<this>>
safeDecode(
data: core.input<this>,
params?: core.ParseContext<core.$ZodIssue>,
): parse.ZodSafeParseResult<core.output<this>>
safeEncodeAsync(
data: core.output<this>,
params?: core.ParseContext<core.$ZodIssue>,
): Promise<parse.ZodSafeParseResult<core.input<this>>>
safeDecodeAsync(
data: core.input<this>,
params?: core.ParseContext<core.$ZodIssue>,
): Promise<parse.ZodSafeParseResult<core.output<this>>>
refine<Ch extends (arg: core.output<this>) => unknown | Promise<unknown>>(
check: Ch,
params?: string | core.$ZodCustomParams,
): Ch extends (arg: any) => arg is infer R ? this & ZodType<R, core.input<this>> : this
superRefine(
refinement: (
arg: core.output<this>,
ctx: core.$RefinementCtx<core.output<this>>,
) => void | Promise<void>,
params?: core.$ZodSuperRefineParams,
): this
overwrite(fn: (x: core.output<this>) => core.output<this>): this
optional(): ZodOptional<this>
exactOptional(): ZodExactOptional<this>
nonoptional(params?: string | core.$ZodNonOptionalParams): ZodNonOptional<this>
nullable(): ZodNullable<this>
nullish(): ZodOptional<ZodNullable<this>>
default(def: util.NoUndefined<core.output<this>>): ZodDefault<this>
default(def: () => util.NoUndefined<core.output<this>>): ZodDefault<this>
prefault(def: () => core.input<this>): ZodPrefault<this>
prefault(def: core.input<this>): ZodPrefault<this>
array(): ZodArray<this>
or<T extends core.SomeType>(option: T): ZodUnion<[this, T]>
and<T extends core.SomeType>(incoming: T): ZodIntersection<this, T>
transform<NewOut>(
transform: (
arg: core.output<this>,
ctx: core.$RefinementCtx<core.output<this>>,
) => NewOut | Promise<NewOut>,
): ZodPipe<this, ZodTransform<Awaited<NewOut>, core.output<this>>>
catch(def: core.output<this>): ZodCatch<this>
catch(def: (ctx: core.$ZodCatchCtx) => core.output<this>): ZodCatch<this>
pipe<T extends core.$ZodType<any, core.output<this>>>(
target: T | core.$ZodType<any, core.output<this>>,
): ZodPipe<this, T>
readonly(): ZodReadonly<this>
/** Returns a new instance that has been registered in `z.globalRegistry` with the specified description */
describe(description: string): this
description?: string
/** Returns the metadata associated with this instance in `z.globalRegistry` */
meta(): core.$replace<core.GlobalMeta, this> | undefined
/** Returns a new instance that has been registered in `z.globalRegistry` with the specified metadata */
meta(data: core.$replace<core.GlobalMeta, this>): this
/** @deprecated Try safe-parsing `undefined` (this is what `isOptional` does internally):
*
* ```ts
* const schema = z.string().optional();
* const isOptional = schema.safeParse(undefined).success; // true
* ```
*/
isOptional(): boolean
/**
* @deprecated Try safe-parsing `null` (this is what `isNullable` does internally):
*
* ```ts
* const schema = z.string().nullable();
* const isNullable = schema.safeParse(null).success; // true
* ```
*/
isNullable(): boolean
apply<T>(fn: (schema: this) => T): T
}
utilities.getValidProps
Validates resource props against a Zod schema, returning typed valid props or throwing a descriptive error with field-level messages prefixed by the resource name.
const getValidProps: <Props>({
name,
schema,
props,
}: {
name: string
schema: ZodSchema<Props>
props: unknown
}) => Props
options— Validation options.options.name— The resource name (used in error messages, e.g.,"User.email: Required").options.schema— The Zod schema to validate against.options.props— The raw props to validate.
Returns: The validated and typed props.
utilities.safeParse
Validates data against a Zod schema without throwing, returning a discriminated union.
const safeParse: <T>(
schema: ZodSchema<T>,
data: unknown,
) => { success: true; data: T } | { success: false; error: ZodError }
schema— The Zod schema to validate against.data— The raw data to validate.
Returns: { success: true, data } on success, or { success: false, error } with a ZodError.
utilities.validate
Validates data against a Zod schema, throwing a ZodError on failure.
const validate: <T>(schema: ZodSchema<T>, data: unknown) => T
schema— The Zod schema to validate against.data— The raw data to validate.
Returns: The validated and typed data.
utilities.isEmail
Basic regex for determining whether or not a string is an email address.
const isEmail: (string: string, strict?: boolean) => boolean
string— The string.strict— Whether strict.
Returns: Whether email.
utilities.isUuid
Basic regex for determining whether or not a string is a UUID.
const isUuid: (uuid: string) => boolean
uuid— The uuid.
Returns: Whether uuid.
Injection Notes
Requirements
Peer dependencies:
@molecule/api-bond^1.0.1@molecule/api-database^1.0.1@molecule/api-i18n^1.0.1@molecule/api-locales-resource^1.0.1@molecule/api-utilities-validation^1.0.1
Runtime Dependencies
@molecule/api-bond@molecule/api-database@molecule/api-i18n@molecule/api-locales-resource@molecule/api-utilities-validationuuidzod
Working WITH installed @molecule/api-resource-* packages (auth getUserId,
table ownership, migrations) — what generated code most often breaks:
- Auth inside a handler is
const userId = getUserId(res), imported from@molecule/api-bonds-default-express— NOT from this package (this package'sutilitiesnamespace holds only validation helpers). Return 401 when it is missing. There is NOrequireAuthmiddleware or file to import or create; auth is applied at the router level. A route's authorizer may also stash the already-loaded row on the response context and hand it toread({ id, props })to skip a second database fetch. - A selected
@molecule/api-resource-*package OWNS its table AND its already-wired routes with a FIXED schema (e.g.api-resource-projectownsprojectskeyed byuser_idwith a live/projectshandler). Do NOT write a migration that re-creates such a table with a different schema — yourCREATE TABLEshadows theirs (or vice-versa) and the package's still-wired handler then 500s (column "user_id" does not exist). To model a different shape, use a DIFFERENT table name (e.g.workspace_projects) + your own handler. Checkapi/migrations/*+api/__setup__/for tables packages already provide. - Adding a resource package AFTER scaffold? Copy its migration. Such a
package ships its table DDL at
node_modules/@molecule/<pkg>/src/__setup__/*.sql— copy it intoapi/migrations/<next-number>_<name>.sql. Wiring its routes/handler alone does NOT create the table: every request 500s with "relation does not exist" even though the build type-checks clean. (Only packages present at scaffold time get their migration auto-created.) - Zod/validation helpers are NAMESPACED exports, not top-level:
schema.basePropsSchema/schema.validate/schema.safeParse/schema.partial/schema.pick, andutilities.getValidProps(re-exported from@molecule/api-utilities-validation). Import the namespace:import { schema, utilities } from '@molecule/api-resource'.
Translations
Translation strings are provided by @molecule/api-locales-resource.