-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore(docs): add usage koa-router and axios-api-client
- Loading branch information
Showing
6 changed files
with
257 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
16 changes: 7 additions & 9 deletions
16
docs/generators/api-clients.md → docs/generators/importing-structure.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
# Axios API client | ||
|
||
As seen at [the targets](/generators/targets.html) Compas supports generating | ||
API clients based on Axios. Even though we support different target languages | ||
(JavaScript and TypeScript) and runtimes (Node.js, browser, React-Native), the | ||
generated interface looks practically the same over all of them. In this | ||
documented we use the Typescript variant for the broswer runtime | ||
|
||
```js | ||
import { Generator } from "@compas/code-gen/experimental"; | ||
|
||
const generator = new Generator(); | ||
|
||
generator.generate({ | ||
targetLanguage: "ts", | ||
outputDirectory: "./src/generated", | ||
generators: { | ||
apiClient: { | ||
target: { | ||
library: "axios", | ||
targetRuntime: "browser", | ||
globalClient: false, | ||
includeWrapper: "react-query", | ||
}, | ||
}, | ||
}, | ||
}); | ||
``` | ||
|
||
## Usage | ||
|
||
Compas generates a specific, fully typed, function for each available route. | ||
|
||
```ts | ||
import { apiUserList } from "./generated/user/apiClient"; | ||
// declare function apiUserList(axiosInstance: AxiosInstance): Promise<UserListResponse>; | ||
|
||
const { users } = await apiUserList(axiosInstance); | ||
|
||
// Extra inputs like route params, query params or a request body is typed as well | ||
// declare function apiUserUpdate(axiosInstance: AxiosInstance, params: { userId: string }, body: { receiveNotifications: boolean }): Promise<UserListResponse>; | ||
await apiUserUpdate( | ||
axiosInstance, | ||
{ userId: "uuid-x" }, | ||
{ receiveNotifications: true }, | ||
); | ||
``` | ||
|
||
## React-query wrapper | ||
|
||
When `includeWrapper` is set to `react-query`, Compas will generate a wrapper | ||
hook around each API client function using | ||
[TanStack Query](https://tanstack.com/query/latest). The above examples can then | ||
be used like | ||
|
||
```tsx | ||
import { ApiProvider } from "./generated/api-client"; | ||
import { useUserList, useUserUpdate } from "./generated/reactQueries"; | ||
|
||
function App() { | ||
// Wrap your React tree with the generated ApiProvder | ||
// This step is not necessary when `globalClients` is set to `true`, see below. | ||
return ( | ||
<ApiProvider | ||
axiosInstance={axios.create({ | ||
/* ... */ | ||
})} | ||
> | ||
<UserList /> | ||
</ApiProvider> | ||
); | ||
} | ||
|
||
function UserList() { | ||
const { data } = useUserList(); | ||
const { mutate: updateUser } = useUserUpdate( | ||
{}, | ||
{ | ||
// Automatically invalidate dependant queries when they are provided in the structure | ||
invalidateQueries: true, | ||
}, | ||
); | ||
|
||
// Use some form library | ||
const { onSubmit } = useForm({ | ||
onSubmit: (values) => { | ||
// Call the mutation | ||
updateUser({ | ||
params: { | ||
userId: values.selectedUser, | ||
}, | ||
body: { | ||
receiveNotifications: value.receiveNotifications, | ||
}, | ||
}); | ||
}, | ||
}); | ||
|
||
return <div>{/* ... */}</div>; | ||
} | ||
``` | ||
|
||
## Global client usage | ||
|
||
If the `globalClient` option is set to `true` in the generate call, Compas will | ||
generate a `common/api-clients.ts` file. This contains a bare Axios (and | ||
react-query `QueryClient`) instance. This removes the need to pass in the | ||
`AxiosInstance` as the first argument on all generated API calls. | ||
|
||
Use this with caution if you do things like server-side-rendering (SSR) for | ||
authenticated pages, as the global client will be reused across different | ||
requests from different users. | ||
|
||
Overwriting options on global clients can be done as well: | ||
|
||
```ts | ||
import { QueryClient } from "@tanstack/react-query"; | ||
import { axiosInstance } from "./generated/api-clients"; | ||
|
||
axiosIntance.defaults.baseURL = "https://my.site"; | ||
|
||
// And if you use the react-query wrapper | ||
import { queryClient } from "./generated/api-clients"; | ||
|
||
queryClient.setDefaultOptions({ | ||
queries: { | ||
retry: 2, | ||
}, | ||
}); | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
# Koa compatible router | ||
|
||
As mentioned in [the targets](/generators/targets.html), Compas supports | ||
generating a [Koa](https://koajs.com/) compatible router. | ||
|
||
```js | ||
import { Generator } from "@compas/code-gen/experimental"; | ||
|
||
const generator = new Generator(); | ||
|
||
// Build your own structure or import an existing one | ||
|
||
generator.generate({ | ||
targetLanguage: "js", | ||
outputDirectory: "./src/generated", | ||
generators: { | ||
router: { | ||
target: { | ||
library: "koa", | ||
}, | ||
exposeApiStructure: true, | ||
}, | ||
}, | ||
}); | ||
``` | ||
|
||
## Setup | ||
|
||
```js | ||
import Koa from "koa"; | ||
import { router } from "./src/generated/common/router.js"; | ||
|
||
const app = new Koa(); | ||
|
||
// Add your own middleware and custom route handlers | ||
app.use((ctx, next) => { | ||
console.log(ctx.method, ctx.path); | ||
return next(); | ||
}); | ||
|
||
// And finally use the generated router middleware | ||
app.use(router()); | ||
|
||
app.listen(3000); | ||
``` | ||
|
||
All your routes are now be accessible but return a `405 Not Implemented` HTTP | ||
status. Let's see how to add route implementations. | ||
|
||
## Implementing controllers | ||
|
||
Implementing the controllers is done by overwriting their generated | ||
implementations. | ||
|
||
```js | ||
// Note how we import the handlers or controller from the generated files | ||
import { userHandlers } from "./src/generated/user/controller.js"; | ||
|
||
userHandlers.single = async (ctx) => { | ||
// ctx is a Koa context. | ||
// Set the response, like you'd normally do in Koa. | ||
ctx.body = { | ||
email: "[email protected]", | ||
}; | ||
}; | ||
|
||
import { imaginaryHandlers } from "./src/generated/imaginary/controller.js"; | ||
|
||
// Validators are integrated in to the generated router. | ||
imaginaryHandlers.route = async (ctx) => { | ||
// `R.get("/:userId", "route").params({ userId: T.uuid() })` | ||
// `GET /e4dbc7dd-adfb-4fce-83f6-f5dcaf68c30c` | ||
// results in `ctx.validatedParams`. The router will automatically return a `400 Bad Request` if the `userId` is not a valid uuid. | ||
ctx.validatedParams.userId; // -> uuid | ||
|
||
// `R.get("/", "route").query({ offset: T.number().default(0) })` | ||
// `GET /?offset=30 | ||
ctx.validatedQuery.offset; // -> number (30) | ||
|
||
// `R.post("/", "route").body({ tags: [T.string()] })` | ||
// Request body is validated as well | ||
ctx.validatedBody.tags; // -> string[] | ||
|
||
// `R.get("/", "route").response({ foo: "bar" })` | ||
// Responses are validated as well. This ensures that you as the API creator adhere to the structure (or contract). | ||
ctx.body = {}; // throws a `500 Internal Server Error`, response did not pass validators | ||
ctx.body = { foo: "bar" }; // returns a `200 OK`. | ||
}; | ||
``` | ||
|
||
## Integrate with other Compas features | ||
|
||
Compas provides a few features to make working with Koa and Compas a more | ||
integrated experience. | ||
|
||
::: details | ||
|
||
Under construction | ||
|
||
- `getApp` from `@compas/server` | ||
- `createBodyParsers` from `@compas/server` | ||
|
||
::: |