Skip to content

Commit

Permalink
Add remark-validate-links and fix some links
Browse files Browse the repository at this point in the history
  • Loading branch information
sodic committed Aug 10, 2023
1 parent 796aadf commit 921b701
Show file tree
Hide file tree
Showing 7 changed files with 4,258 additions and 598 deletions.
54 changes: 35 additions & 19 deletions web/docs/advanced/apis.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
title: Custom HTTP API Endpoints
---

import { ShowForTs, ShowForJs } from '@site/src/components/TsJsHelpers';

In Wasp, the default client-server interaction mechanism is through [Operations](/docs/data-model/operations/overview). However, if you need a specific URL method/path, or a specific response, Operations may not be suitable for you. For these cases, you can use an `api`! Best of all, they should look and feel very familiar.
Expand All @@ -10,12 +11,14 @@ In Wasp, the default client-server interaction mechanism is through [Operations]
APIs are used to tie a JS function to an certain endpoint e.g. `POST /something/special`. They are distinct from Operations and have no client-side helpers (like `useQuery`).

To create a Wasp API, you must:

1. Declare the API in Wasp using the `api` declaration
2. Define the API's NodeJS implementation

After completing these two steps, you'll be able to call the API from the client code (via our `Axios` wrapper), or from the outside world.

### Declaring the API in Wasp

First we need to declare the API in the Wasp file and you can easily do this with the `api` declaration:

```wasp title="main.wasp"
Expand All @@ -28,12 +31,13 @@ api fooBar { // APIs and their implementations don't need to (but can) have the
```

The `api` declaration supports the following fields:

- `fn: ServerImport` (required) - The import statement of the APIs NodeJs implementation.
- `httpRoute: (HttpMethod, string)` (required) - The HTTP (method, path) pair, where the method can be one of:
- `ALL`, `GET`, `POST`, `PUT` or `DELETE`
- and path is an Express path `string`.
- `entities: [Entity]` (optional) - A list of entities you wish to use inside your API.
We'll leave this option aside for now. You can read more about it [here](#using-entities-in-apis).
We'll leave this option aside for now. You can read more about it [here](#using-entities-in-apis).
- `auth: bool` (optional) - If auth is enabled, this will default to `true` and provide a `context.user` object. If you do not wish to attempt to parse the JWT in the Authorization Header, you may set this to `false`.
- `middlewareConfigFn: ServerImport` (optional) - The import statement to an Express middleware config function for this API. See [the guide here](/docs/advanced/middleware-config).

Expand All @@ -42,12 +46,13 @@ We'll leave this option aside for now. You can read more about it [here](#using-
<ShowForTs>

:::note
To make sure the Wasp compiler generates the types for APIs for use in the NodeJS implementation, you should add your `api` declarations to your `.wasp` file first _and_ keep the `wasp start` command running.
To make sure the Wasp compiler generates the types for APIs for use in the NodeJS implementation, you should add your `api` declarations to your `.wasp` file first _and_ keep the `wasp start` command running.
:::
</ShowForTs>

After you defined the API, it should be implemented as a NodeJS function that takes three arguments:
1. `req`: Express Request object

1. `req`: Express Request object
2. `res`: Express Response object
3. `context`: An additional context object **injected into the API by Wasp**. This object contains user session information, as well as information about entities. The examples here won't use the context for simplicity purposes. You can read more about it in the [section about using entities in APIs](#using-entities-in-apis).

Expand All @@ -57,9 +62,10 @@ After you defined the API, it should be implemented as a NodeJS function that ta
```ts title="src/server/apis.js"
export const fooBar = (req, res, context) => {
res.set('Access-Control-Allow-Origin', '*') // Example of modifying headers to override Wasp default CORS middleware.
res.json({ msg: `Hello, ${context.user?.username || "stranger"}!` })
res.json({ msg: `Hello, ${context.user?.username || 'stranger'}!` })
}
```

</TabItem>
<TabItem value="ts" label="TypeScript">

Expand All @@ -68,9 +74,10 @@ import { FooBar } from '@wasp/apis/types' // This type is generated by Wasp base
export const fooBar: FooBar = (req, res, context) => {
res.set('Access-Control-Allow-Origin', '*') // Example of modifying headers to override Wasp default CORS middleware.
res.json({ msg: `Hello, ${context.user?.username || "stranger"}!` })
res.json({ msg: `Hello, ${context.user?.username || 'stranger'}!` })
}
```

</TabItem>
</Tabs>

Expand Down Expand Up @@ -98,26 +105,28 @@ We can use the `FooBar` type to which we'll provide the generic **params** and *
import { FooBar } from '@wasp/apis/types'

export const fooBar: FooBar<
{ email: string }, // params
{ answer: number } // response
{ email: string }, // params
{ answer: number } // response
> = (req, res, _context) => {
console.log(req.params.email)
res.json({ answer: 42 })
}
```

</ShowForTs>

## Using the API

### Using the API externally

To use the API externally, you simply call the endpoint using the method and path you used.
To use the API externally, you simply call the endpoint using the method and path you used.

For example, if your app is running at `https://example.com` then from the above you could issue a `GET` to `https://example/com/foo/callback` (in your browser, Postman, `curl`, another web service, etc.).

### Using the API from the client

To use the API from your client, including with auth support, you can import the Axios wrapper from `@wasp/api` and invoke a call. For example:

```ts
import React, { useEffect } from 'react'
import api from '@wasp/api'
Expand All @@ -130,17 +139,14 @@ async function fetchCustomRoute() {
export const Foo = () => {
useEffect(() => {
fetchCustomRoute()
}, []);
}, [])

return (
<>
// ...
</>
)
return <>// ...</>
}
```

#### Making sure CORS works

APIs are designed to be as flexible as possible, hence they don't utilize the default middleware like Operations do. As a result, to use these APIs on the client side, you must ensure that CORS (Cross-Origin Resource Sharing) is enabled.

You can do this by defining custom middleware for your APIs in the Wasp file.
Expand All @@ -149,45 +155,53 @@ You can do this by defining custom middleware for your APIs in the Wasp file.
<TabItem value="js" label="JavaScript">

For example, an `apiNamespace` is a simple declaration used to apply some `middlewareConfigFn` to all APIs under some specific path:

```wasp title="main.wasp"
apiNamespace fooBar {
middlewareConfigFn: import { fooBarNamespaceMiddlewareFn } from "@server/apis.js",
path: "/foo"
}
```

And then in the implementation file:

```js title="src/server/apis.js"
export const apiMiddleware = (config) => {
return config;
return config
}
```

</TabItem>
<TabItem value="ts" label="TypeScript">

For example, an `apiNamespace` is a simple declaration used to apply some `middlewareConfigFn` to all APIs under some specific path:

```wasp title="main.wasp"
apiNamespace fooBar {
middlewareConfigFn: import { fooBarNamespaceMiddlewareFn } from "@server/apis.js",
path: "/foo"
}
```

And then in the implementation file (returning the default config):

```ts title="src/server/apis.ts"
import { MiddlewareConfigFn } from "@wasp/middleware"
import { MiddlewareConfigFn } from '@wasp/middleware'
export const apiMiddleware: MiddlewareConfigFn = (config) => {
return config;
return config
}
```

</TabItem>
</Tabs>

We are returning the default middleware which enables CORS for all APIs under the `/foo` path.

For more information about middleware configuration, please see: [Middleware Configuration](/docs/advanced/middleware-config)


## Using Entities in APIs
In many cases, resources used in APIs will be [Entities](#entity).

In many cases, resources used in APIs will be [Entities](/docs/data-model/entities.md).
To use an Entity in your API, add it to the `api` declaration in Wasp:

```wasp {3} title="main.wasp"
Expand All @@ -208,6 +222,7 @@ export const fooBar = (req, res, context) => {
res.json({ count: await context.entities.Task.count() })
}
```

</TabItem>
<TabItem value="ts" label="TypeScript">

Expand All @@ -218,6 +233,7 @@ export const fooBar: FooBar = (req, res, context) => {
res.json({ count: await context.entities.Task.count() })
}
```

</TabItem>
</Tabs>

Expand Down
Loading

0 comments on commit 921b701

Please sign in to comment.