import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import { InstallTabs } from '@site/src/components/InstallTabs';

# Quickstart

Embarking on your journey with `ts-rest` is a breeze! You have the liberty to utilize just the front-end, the backend, or merely the contract (should you choose to do so).

Once you've mastered the fundamentals, you'll find the developer experience to be smooth as silk. ✨

## Installation

Install the core package and zod - Some of our generics rely on zod being installed, so make sure to install it (even as a dev dependency if you don't plan to use Zod)

If you don't install zod, some confusing errors may appear but it should mostly work [#303](https://github.com/ts-rest/ts-rest/issues/303)

<InstallTabs packageName="@ts-rest/core zod" />

## Create a contract

<Tabs groupId="contract">
  <TabItem value="zod" label="Zod">

<p>
  This should ideally be shared between your consumers and producers, e.g. in a
  shared library in a monorepo, or a shared npm package. Think of this as your
  HTTP Schema that both your client and backend can use.
</p>

:::caution Make sure to use `strict` with Zod
Enable `strict` in your `tsconfig.json`! This is required as part of [Zod](https://github.com/colinhacks/zod#requirements)

```
  "compilerOptions": {
    ...
    "strict": true
  }
```

If you don't enable strict mode, ts-rest will still work, but you may face performance issues [#162](https://github.com/ts-rest/ts-rest/issues/162)

:::

```typescript
// contract.ts

import { initContract } from '@ts-rest/core';
import { z } from 'zod';

const c = initContract();

const PostSchema = z.object({
  id: z.string(),
  title: z.string(),
  body: z.string(),
});

export const contract = c.router({
  createPost: {
    method: 'POST',
    path: '/posts',
    responses: {
      201: PostSchema,
    },
    body: z.object({
      title: z.string(),
      body: z.string(),
    }),
    summary: 'Create a post',
  },
  getPost: {
    method: 'GET',
    path: `/posts/:id`,
    responses: {
      200: PostSchema.nullable(),
    },
    summary: 'Get a post by id',
  },
});
```

<p>
  Zod also has some nice features, like enabling body parsing and OpenAPI type
  generation.
</p>

  </TabItem>
  <TabItem value="basic" label="Basic">
Create a contract, this should ideally be shared between your consumers and producers, e.g. in a shared library in a monorepo, or a shared npm package.

```typescript
// contract.ts

import { initContract } from '@ts-rest/core';

const c = initContract();

export const contract = c.router({
  createPost: {
    method: 'POST',
    path: '/posts',
    responses: {
      201: c.type<Post>(),
    },
    body: c.type<{title: string}>(),
    summary: 'Create a post',
  },
  getPost: {
    method: 'GET',
    path: `/posts/:id`,
    responses: {
      200: c.type<Post | null>(),
    },
    summary: 'Get a post by id',
  },
});
```

  </TabItem>
</Tabs>

## Server Implementation

<Tabs groupId="library">
  <TabItem value="nest" label="Nest.js">
  <InstallTabs packageName="@ts-rest/nest" />
<p>ts-rest offers a unique way to create a fully type safe REST API server,
normally Nest APIs are extremely powerful, but hard to make type safe.</p>

Let's add `@ts-rest/nest` to a basic Nest controller:

```typescript
// post.controller.ts

const c = nestControllerContract(contract);
type RequestShapes = NestRequestShapes<typeof c>;

@Controller()
export class PostController implements NestControllerInterface<typeof c> {
  constructor(private readonly postService: PostService) {}

  @TsRest(c.getPost)
  async getPost(@TsRestRequest() { params: { id } }: RequestShapes['getPost']) {
    const post = await this.postService.getPost(id);

    return { status: 200 as const, body: post };
  }

  @TsRest(c.createPost)
  async createPost(@TsRestRequest() { body }: RequestShapes['createPost']) {
    const post = await this.postService.createPost({
      title: body.title,
      body: body.body,
    });

    return { status: 201 as const, body: post };
  }
}
```

You can see that we're using the _runtime_ object `c` in the `TsRest` decorator to automatically declare your path from the contract's `getPost` route.
We're also using the `RequestShapes` _TypeScript Types_ (which comes from the runtime object `c`) to ensure type safety of your contract _on_ the Nest controller.

If you were to change the `body` return type to `{ body: true }` for example, this will give you a typescript error: Your `body` is defined as an _object_ in the contract above, not boolean!

  </TabItem>
  <TabItem value="express" label="Express">
  <InstallTabs packageName="@ts-rest/express" />
<p>The express implementaton allows full type safety, offering; body parsing, query parsing, param parsing and full error handling</p>

```typescript
// main.ts

import { initServer } from '@ts-rest/express';
const app = express();

app.use(cors());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

const s = initServer();

const router = s.router(contract, {
  getPost: async ({ params: { id } }) => {
    const post = await prisma.post.findUnique({ where: { id } });

    return {
      status: 200,
      body: post,
    };
  },
  createPost: async ({ body }) => {
    const post = await prisma.post.create({
      data: body,
    });

    return {
      status: 201,
      body: post,
    };
  },
});

createExpressEndpoints(contract, router, app);

const port = process.env.port || 3333;
const server = app.listen(port, () => {
  console.log(`Listening at http://localhost:${port}`);
});
```

  </TabItem>
   <TabItem value="fastify" label="Fastify">
  <InstallTabs packageName="@ts-rest/fastify" />
<p>The fastify implementaton allows full type safety, offering; body parsing, query parsing, param parsing and full error handling</p>

```typescript
// main.ts

import { initServer } from '@ts-rest/fastify';
const app = fastify();

const s = initServer();

const router = s.router(contract, {
  getPost: async ({ params: { id } }) => {
    const post = await prisma.post.findUnique({ where: { id } });

    return {
      status: 200,
      body: post,
    };
  },
  createPost: async ({ body }) => {
    const post = await prisma.post.create({
      data: body,
    });

    return {
      status: 201,
      body: post,
    };
  },
});

app.register(s.plugin(router));

const start = async () => {
  try {
    await app.listen({ port: 3000 });
  } catch (err) {
    app.log.error(err);
    process.exit(1);
  }
};

start();
```

  </TabItem>
  <TabItem value="next" label="Next.js">
  <InstallTabs packageName="@ts-rest/next" />

```typescript
// pages/api/[...ts-rest].tsx

// `contract` is the AppRouter returned by `c.router`
const postsRouter = createNextRoute(contract.posts, {
  createPost: async (args) => {
    const newPost = await posts.createPost(args.body);

    return {
      status: 201,
      body: newPost,
    };
  },
});

const router = createNextRoute(contract, {
  posts: postsRouter,
});

// Actually initiate the collective endpoints
export default createNextRouter(contract, router);
```

  </TabItem>
</Tabs>

## Client Implementation

<Tabs groupId="client">
  <TabItem value="fetch" label="Fetch">
    <p>
      This is the basic client, using fetch under the hood which is exported
      from <code>@ts-rest/core</code>.
    </p>

```typescript
// client.ts
import { initClient } from "@ts-rest/core";

// `contract` is the AppRouter returned by `c.router`
const client = initClient(contract, {
  baseUrl: 'http://localhost:3000',
  baseHeaders: {},
});

const { body, status } = await client.createPost({
  body: {
    title: 'Post Title',
    body: 'Post Body',
  },
});

if (status === 201) {
  // body is Post
  console.log(body);
} else {
  // body is unknown
  console.log(body);
}
```

  </TabItem>
  <TabItem value="react-query" label="React Query">
  <InstallTabs packageName="@ts-rest/react-query" />

```tsx
// client.ts
import { initQueryClient } from "@ts-rest/react-query";

// `contract` is the AppRouter returned by `c.router`
export const client = initQueryClient(contract, {
  baseUrl: 'http://localhost:3333',
  baseHeaders: {},
});

export const Index = () => {
  const { data, isLoading, error } = client.getPost.useQuery(["posts/1"], {
    params: { id: '1' },
  });

  if (isLoading) {
    return <div>Loading...</div>;
  }

  if (data.status !== 200 || error) {
    return <div>Error</div>;
  }

  return <div>{data.body.title}</div>;
};
```

:::info

The response from react-query is typed as follows:

If status is 2XX, it's part of the "data" return. If it's any other status code it's part of the "error" return e.g.

```typescript
const data:
  | {
      status: 200;
      body: Post | null;
    }
  | undefined;
```

```typescript
const error:
  | {
      status: 404;
      body: null
    }
  | {
      status: 404 | 100 | 101 | 102 | 300 | 301 | 302 | 303 | 304 | 305 | 307 | 308 | 400 | 401 | 402 | 403 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | ... 16 more ... | 511;
      body: unknown;
    }
  | null
```

:::

  </TabItem>
</Tabs>
