verdad
v1.0.35
Published
Define a single source of truth for your API and persistence layer, and use Verdad's extensions to a) call it, b) implement it, and c) deploy it.
Downloads
2
Keywords
Readme
Verdad
Verdad lets you:
- ✅ write your API once, with support for multiple base URLs and fully typed parameters & bodies (using io-ts)
- ✅ implement and deploy your API to any infrastructure using built-in or custom extensions
- ✅ call your API from the client with zero duplication and no synchonization headaches
Step 1
Define your backend API in Verdad:
import * as t from 'io-ts'
import { StatusCodes } from "http-status-codes";
import { NumberFromString } from 'io-ts-types';
import { VerdadRESTAPI } from "verdad";
export const musicAPI = VerdadRESTAPI.api({
name: 'my New Music Startup',
servers: {
prod: 'https://api.music.com',
test: 'https://test-api.music.com',
},
builder: (ctx) => ({
playlists: VerdadRESTAPI.resource(ctx, ['users', { parameter: 'userID' }, 'playlists'], {
get: (ctx) => VerdadRESTAPI.method(ctx, {
pathParametersType: t.type({ userID: t.string }),
queryParametersType: t.partial({ pageNumber: NumberFromString }),
headerParametersType: t.type({ 'authorization-token': t.string, }),
requestBodyType: t.null,
successResponse: {
statusCodes: [
StatusCodes.OK as const
],
bodyType: t.array(PlaylistModel)
},
errorResponse: {
statusCodes: [
StatusCodes.UNAUTHORIZED as const,
StatusCodes.BAD_REQUEST as const,
StatusCodes.INTERNAL_SERVER_ERROR as const,
],
bodyType: t.type({ errorDetails: t.string }),
}
}),
post: () => undefined,
delete: () => undefined,
patch: () => undefined,
put: () => undefined,
}),
// albums: ...,
})
})
Step 2
Use an extension to deploy this API to your infrastructure of choice:
For example, to deploy to AWS Lambda:
- Call
VerdadCloudFormation.makeServerlessFunctions()
from yourserverless.ts
file:
import type { AWS } from '@serverless/typescript';
import { VerdadCloudFormation } from 'verdad/lib/extensions';
const serverlessConfig: AWS = {
service: 'music-api',
provider: { name: 'aws' },
functions: VerdadCloudFormation.makeServerlessFunctions(musicAPI),
};
module.exports = serverlessConfig;
- Implement each of your API methods.
makeServerlessFunctions()
looks for the implementations undersrc/resources/<path>/<method>
. For theusers/*/playlists
GET method example above, it would look for an implementation insrc/resources/users/playlists/get.ts
:
export const { verdadMain } = implement(
musicAPI.resources.playlists.get,
async (input) => {
const playlists = retrievePlaylists(
input.pathParameters.userID,
input.headerParameters['authorization-token'],
input.queryParameters.pageNumber,
)
return E.right({
statusCode: StatusCodes.OK,
body: {
value: playlists,
type: t.array(PlaylistModel)
}
})
},
(error: LambdaRuntimeError) => {
error // <-- access error
// Error is a discriminated union; use switch to handle specific error scenarios
return {
statusCode: StatusCodes.INTERNAL_SERVER_ERROR,
body: {
value: { errorDetails: 'Details hidden for security' },
type: t.type({ errorDetails: t.string }),
}
}
}
)
Step 3
Use a client extension to call the APIs:
For example, to make calls using Axios:
import { VerdadAxios } from './extensions/axios/Axios';
async function getPlaylists(userID: string, authToken: string) {
const musicAPIAxios = new VerdadAxios.RESTAPI({
api: musicAPI,
server: 'prod',
debug: {} // Permits logger and custom HTTP agent to support MitM intercepts
})
const playlists = await musicAPIAxios.callMethod(api => api.playlists.get, {
pathParameters: { userID },
queryParameters: {},
headerParameters: {
'authorization-token': authToken
},
body: null,
})
if (E.isRight(playlists)) {
displayToUser(playlists.right.successResponse)
} else {
playlists.left // <-- access error
// Error is a discriminated union; use switch to handle specific error scenarios
console.log("Could not load playlists from backend API")
}
}