Getting Started
Get up and running with fossyl in minutes.
Installation
Section titled “Installation”Install fossyl using your preferred package manager:
npm install fossylpnpm add fossylyarn add fossylBasic Usage
Section titled “Basic Usage”Create your first route with fossyl:
export const getUserRoute = {
path: string
method: RestMethod
steps: Steps[]
handler: Function
authenticator?: AuthenticationFunction<any>
validator?: ValidatorFunction<any>
queryValidator?: ValidatorFunction<any>
urlParamValidator?: ValidatorFunction<any>
paginationConfig?: PaginationConfig
hasTransaction: boolean
} = routerRouter<"/api/users"> .createEndpoint<Path extends `/api/users${string}`>(
path: Path,
) => Endpoint<Path, true>("/api/users/:id") .get<Response extends ResponseData>(
handler: (
parameters: { url: { id: string } & { readonly __kind: "url" } } & {
readonly __kind: "parameters"
},
) => () => Promise<Response>,
) => Route((params{ url: { id: string } & { readonly __kind: "url" } } & {
readonly __kind: "parameters"
}) => async () => { const userUserRow = {
id: number
name: string
email: string
created_at: string
} = await userService.getUserRoute = {
path: string
method: RestMethod
steps: Steps[]
handler: Function
authenticator?: AuthenticationFunction<any>
validator?: ValidatorFunction<any>
queryValidator?: ValidatorFunction<any>
urlParamValidator?: ValidatorFunction<any>
paginationConfig?: PaginationConfig
hasTransaction: boolean
}(Number(params{ url: { id: string } & { readonly __kind: "url" } } & {
readonly __kind: "parameters"
}.url{ id: string } & { readonly __kind: "url" }.idstring)); return { typeName: "User" as const, ...userUserRow = {
id: number
name: string
email: string
created_at: string
} }; });Type Safety
Section titled “Type Safety”fossyl provides full type inference for your routes:
export const getUserRoute = {
path: string
method: RestMethod
steps: Steps[]
handler: Function
authenticator?: AuthenticationFunction<any>
validator?: ValidatorFunction<any>
queryValidator?: ValidatorFunction<any>
urlParamValidator?: ValidatorFunction<any>
paginationConfig?: PaginationConfig
hasTransaction: boolean
} = routerRouter<"/api/users"> .createEndpoint<Path extends `/api/users${string}`>(
path: Path,
) => Endpoint<Path, true>("/api/users/:id") .get<Response extends ResponseData>(
handler: (
parameters: { url: { id: string } & { readonly __kind: "url" } } & {
readonly __kind: "parameters"
},
) => () => Promise<Response>,
) => Route((params{ url: { id: string } & { readonly __kind: "url" } } & {
readonly __kind: "parameters"
}) => async () => { const userUserRow = {
id: number
name: string
email: string
created_at: string
} = await userService.getUserRoute = {
path: string
method: RestMethod
steps: Steps[]
handler: Function
authenticator?: AuthenticationFunction<any>
validator?: ValidatorFunction<any>
queryValidator?: ValidatorFunction<any>
urlParamValidator?: ValidatorFunction<any>
paginationConfig?: PaginationConfig
hasTransaction: boolean
}(Number(params{ url: { id: string } & { readonly __kind: "url" } } & {
readonly __kind: "parameters"
}.url{ id: string } & { readonly __kind: "url" }.idstring)); return { typeName: "User" as const, ...userUserRow = {
id: number
name: string
email: string
created_at: string
} }; });Adding Validation
Section titled “Adding Validation”Use any validation library you prefer:
export const createUserRoute = {
path: string
method: RestMethod
steps: Steps[]
handler: Function
authenticator?: AuthenticationFunction<any>
validator?: ValidatorFunction<any>
queryValidator?: ValidatorFunction<any>
urlParamValidator?: ValidatorFunction<any>
paginationConfig?: PaginationConfig
hasTransaction: boolean
} = routerRouter<"/api/users"> .createEndpoint<Path extends `/api/users${string}`>(
path: Path,
) => Endpoint<Path, true>("/api/users") .authenticator(
headers: Record<string, string>,
) => Promise<{ userId: string } & Authentication>(authenticator(
headers: Record<string, string>,
) => Promise<{ userId: string } & Authentication>) .validator<RequestBody extends unknown>(
validatorFunction: ValidatorFunction<RequestBody>,
) => {
post: <Response extends ResponseData>(
handler: (
auth: { userId: string } & Authentication & { readonly __kind: "auth" },
) => undefined extends RequestBody
? () => Promise<Response>
: (
body: RequestBody & { readonly __kind: "body" },
) => () => Promise<Response>,
) => Route
put: <Response extends ResponseData>(
handler: (
auth: { userId: string } & Authentication & { readonly __kind: "auth" },
) => undefined extends RequestBody
? () => Promise<Response>
: (
body: RequestBody & { readonly __kind: "body" },
) => () => Promise<Response>,
) => Route
}(createUserValidator(
data: unknown,
params?: util.InexactPartial<ParseParams>,
) => { name: string; email: string }) .post<Response extends ResponseData>(
handler: (
auth: { userId: string } & Authentication & { readonly __kind: "auth" },
) => (
body: { name: string; email: string } & { readonly __kind: "body" },
) => () => Promise<Response>,
) => Route((_auth{ userId: string } & Authentication & { readonly __kind: "auth" }) => (body{ name: string; email: string } & { readonly __kind: "body" }) => async () => { const userUserRow = {
id: number
name: string
email: string
created_at: string
} = await userService.createUserRoute = {
path: string
method: RestMethod
steps: Steps[]
handler: Function
authenticator?: AuthenticationFunction<any>
validator?: ValidatorFunction<any>
queryValidator?: ValidatorFunction<any>
urlParamValidator?: ValidatorFunction<any>
paginationConfig?: PaginationConfig
hasTransaction: boolean
}(body{ name: string; email: string } & { readonly __kind: "body" }.namestring, body{ name: string; email: string } & { readonly __kind: "body" }.emailstring); return { typeName: "User" as const, ...userUserRow = {
id: number
name: string
email: string
created_at: string
} }; });Query Parameters
Section titled “Query Parameters”Add type-safe query parameter validation:
export const searchTodosRoute = {
path: string
method: RestMethod
steps: Steps[]
handler: Function
authenticator?: AuthenticationFunction<any>
validator?: ValidatorFunction<any>
queryValidator?: ValidatorFunction<any>
urlParamValidator?: ValidatorFunction<any>
paginationConfig?: PaginationConfig
hasTransaction: boolean
} = routerRouter<"/api/todos"> .createEndpoint<Path extends `/api/todos${string}`>(
path: Path,
) => Endpoint<Path, true>("/api/todos/search") .query{ q: string; limit?: number; offset?: number } & {
readonly __kind: "query"
}((data{ typeName: "Todo"; id: number }[]): { qstring: string; limitnumber | undefined?: number; offsetnumber | undefined?: number } => { const paramsRecord<string, string | undefined> = data{ typeName: "Todo"; id: number }[] as Record<string, string | undefinedundefined>; if (!paramsRecord<string, string | undefined>.qstring || paramsRecord<string, string | undefined>.qstring.trim() => string() === "") { throw new Error('Search query "q" is required'); } return { q: paramsRecord<string, string | undefined>.qstring, limit: paramsRecord<string, string | undefined>.limitnumber | undefined ? Number(paramsRecord<string, string | undefined>.limitnumber | undefined) : undefinedundefined, offset: paramsRecord<string, string | undefined>.offsetnumber | undefined ? Number(paramsRecord<string, string | undefined>.offsetnumber | undefined) : undefinedundefined, }; }) .get<Response extends ResponseData>(
handler: (
parameters: {
query: { q: string; limit?: number; offset?: number } & {
readonly __kind: "query"
}
} & { readonly __kind: "parameters" },
) => () => Promise<Response>,
) => Route(({ query{ q: string; limit?: number; offset?: number } & {
readonly __kind: "query"
} }) => async () => { return { typeName: "SearchResult" as const, q: query{ q: string; limit?: number; offset?: number } & {
readonly __kind: "query"
}.qstring, limit: query{ q: string; limit?: number; offset?: number } & {
readonly __kind: "query"
}.limitnumber | undefined, offset: query{ q: string; limit?: number; offset?: number } & {
readonly __kind: "query"
}.offsetnumber | undefined, }; });Authentication
Section titled “Authentication”Add type-safe authentication to your routes:
export const getTodoRoute = {
path: string
method: RestMethod
steps: Steps[]
handler: Function
authenticator?: AuthenticationFunction<any>
validator?: ValidatorFunction<any>
queryValidator?: ValidatorFunction<any>
urlParamValidator?: ValidatorFunction<any>
paginationConfig?: PaginationConfig
hasTransaction: boolean
} = routerRouter<"/api/todos"> .createEndpoint<Path extends `/api/todos${string}`>(
path: Path,
) => Endpoint<Path, true>("/api/todos/:id") .authenticator(
headers: Record<string, string>,
) => Promise<{ userId: string } & Authentication>(authenticator(
headers: Record<string, string>,
) => Promise<{ userId: string } & Authentication>) .get<Response extends ResponseData>(
handler: (
parameters: {
query: { q: string; limit?: number; offset?: number } & {
readonly __kind: "query"
}
} & { readonly __kind: "parameters" },
) => () => Promise<Response>,
) => Route((paramsRecord<string, string | undefined>) => (_auth{ userId: string } & Authentication & { readonly __kind: "auth" }) => async () => { const todoTodoRow = {
id: number
title: string
completed: number
created_at: string
} = await todoService.getTodoRoute = {
path: string
method: RestMethod
steps: Steps[]
handler: Function
authenticator?: AuthenticationFunction<any>
validator?: ValidatorFunction<any>
queryValidator?: ValidatorFunction<any>
urlParamValidator?: ValidatorFunction<any>
paginationConfig?: PaginationConfig
hasTransaction: boolean
}(Number(paramsRecord<string, string | undefined>.url{ id: string } & { readonly __kind: "url" }.idnumber)); return { typeName: "Todo" as const, ...todoTodoRow = {
id: number
title: string
completed: number
created_at: string
} }; });Next Steps
Section titled “Next Steps”- Explore the API Reference for detailed documentation
- Check out the GitHub repository for examples
- Join the community and contribute!