Skip to content

Commit

Permalink
feat: added typescript support and updated docs accordingly (#94)
Browse files Browse the repository at this point in the history
* feat: added typescript support and updated docs accordingly

* feat: added typescript support and updated docs accordingly

* fix: added a line to the README about typescript support

* fix: added "types" field in package.json

* added store module declaration

Co-authored-by: Daniel Roe <daniel@roe.dev>

* refactored user type into a Record

Co-authored-by: Daniel Roe <daniel@roe.dev>

* fix: linted code samples in the docs, refactored NuxtStrapiQueryParams into a Record type

* fix: removed line from index.md about ts, added declaration for NuxtAppOptions

* fix: made a link in the ts page relative to the current url; removed declaration imports

Co-authored-by: Daniel Roe <daniel@roe.dev>
  • Loading branch information
LuckeeDev and danielroe committed Jan 8, 2021
1 parent 690bf64 commit 64f1927
Show file tree
Hide file tree
Showing 8 changed files with 546 additions and 5 deletions.
3 changes: 3 additions & 0 deletions .eslintignore
Expand Up @@ -6,3 +6,6 @@ coverage

# Plugin
lib/plugin.js

# Typings
/**/*.d.ts
1 change: 1 addition & 0 deletions README.md
Expand Up @@ -20,6 +20,7 @@
- RESTful methods
- Adaptive SDK for API entities
- Handle errors with hooks
- TypeScript support

[📖 &nbsp;Read the documentation](https://strapi.nuxtjs.org)

Expand Down
1 change: 1 addition & 0 deletions docs/content/en/index.md
Expand Up @@ -8,6 +8,7 @@ features:
- RESTful methods
- Adaptive SDK for API entities
- Handle errors with hooks
- TypeScript support
---

<img src="/preview.png" class="light-img" width="1280" height="640" alt=""/>
Expand Down
2 changes: 1 addition & 1 deletion docs/content/en/proxy.md
@@ -1,7 +1,7 @@
---
title: Using a proxy
description: 'Use Strapi behind a proxy with Nuxt Proxy module'
position: 7
position: 8
category: Advanced
fullscreen: true
---
Expand Down
155 changes: 155 additions & 0 deletions docs/content/en/typescript.md
@@ -0,0 +1,155 @@
---
title: Usage with Typescript
description: 'Discover how you can setup your project to integrate Strapi with TypeScript'
position: 7
category: Advanced
---

## Setup

Thanks to [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html),
you can tell the TypeScript compiler where to find the `$strapi` types by adding these lines to your
`tsconfig.json`.

```json[tsconfig.json]
{
"compilerOptions": {
"types": [
"@nuxtjs/strapi"
]
}
}
```

## Usage

You now have access to `this.$strapi` inside your components and to `ctx.$strapi` inside
`asyncData`, `fetch`, `plugins`, `middlewares` and `nuxtServerInit`.

### In component methods
```vue
<script lang="ts">
import Vue from 'vue'
interface BlogPost {
title: string;
description: string;
}
export default Vue.extend({
async data () {
const post = await this.$strapi.create<BlogPost>(
'posts',
{
title: 'Welcome to Strapi',
description: 'Strapi is awesome!'
}
)
return {
post
}
}
})
</script>
```

> Notice how you can define the type of the query parameters and of the returned value using generics.
### Inside methods that use `context`
```vue
<script lang="ts">
import Vue from 'vue'
import { Context } from '@nuxt/types'
interface BlogPost {
title: string;
description: string;
}
export default Vue.extend({
async asyncData (ctx: Context) {
const posts = await ctx.$strapi.find<BlogPost[]>(
'posts'
)
return {
posts
}
}
})
</script>
```

## Known issues

There are some known issues with the current implementation of TypeScript in the library,
so we have listed some workarounds.

### Entity shortcuts

The current TypeScript implementation doesn't support entity shortcuts: your code may work,
but it will throw type checking errors. This is why we made the `entity` parameter needed in
every method which involves a query.

You can however define the entities available to your Vue methods with
[string literal types](https://www.typescriptlang.org/docs/handbook/literal-types.html#string-literal-types)
as described below.

```vue
<script lang="ts">
import Vue from 'vue'
import { Context } from '@nuxt/types'
interface BlogPost {
title: string;
description: string;
}
type Entities = 'posts' | 'projects'
export default Vue.extend({
async asyncData (ctx: Context) {
const posts = await ctx.$strapi.find<BlogPost[], Entities>(
'posts'
)
return {
posts
}
}
})
</script>
```

### Query params

Some query methods won't be accepted by the compiler if you define generics as in `$strapi.find<BlogPost[]>`
(e.g.: the array methods described [here](/strapi#findentity-params)).

You can get around this issue by doing something like below.

```vue
<script lang="ts">
import Vue from 'vue'
interface BlogPost {
title: string;
description: string;
}
export default Vue.extend({
async asyncData (ctx: Context) {
const posts: BlogPost[] = await this.$strapi.find(
'posts'
)
return {
posts
}
}
})
</script>
```

> You can always declare the return type by assigning a type to the variable instead of the method.
168 changes: 168 additions & 0 deletions lib/index.d.ts
@@ -0,0 +1,168 @@
import { NuxtHTTPInstance } from '@nuxt/http';
import { NuxtCookies } from 'cookie-universal-nuxt';

type NuxtStrapiQueryParams<T> = T | Record<string, any>;

interface NuxtStrapiGraphQLParams {
query: string;
}

interface NuxtStrapiRegistrationData {
username: string;
email: string;
password: string;
}

interface NuxtStrapiLoginData {
/**
* Can be either the email or the username set by the user.
* */
identifier: string;
password: string;
}

interface NuxtStrapiEmailData {
email: string;
}

interface NuxtStrapiResetPasswordData {
code: string;
password: string;
passwordConfirmation: string;
}

export interface NuxtStrapi {
/**
* Use this object to access details about the
* authenticated user or to directly set a user prop.
* */
user: Record<string, any>;

/**
* Get entries.
* Returns entries matching the query filters.
* You can read more about parameters
* [here](https://strapi.io/documentation/developer-docs/latest/content-api/parameters.html).
* */
find<T = any, Entities = string>(entity: Entities, params?: NuxtStrapiQueryParams<T> ): Promise<T>;

/**
* Count entries.
* Returns the count of entries matching the query filters.
* You can read more about parameters
* [here](https://strapi.io/documentation/developer-docs/latest/content-api/parameters.html).
* */
count<T = any, Entities = string>(entity: Entities, params?: NuxtStrapiQueryParams<T>): Promise<number>;

/**
* Get an entry by id and returns its value.
* */
findOne<T = any, Entities = string>(entity: Entities, id?: string): Promise<T>;

/**
* Creates an entry and returns its value.
* */
create<T = any, Entities = string>(entity: Entities, data?: NuxtStrapiQueryParams<T>): Promise<T>;

/**
* Partially updates an entry by id and returns its value.
* Fields that aren't sent in the query are not changed in the db.
* Send a null value if you want to clear them.
* */
update<T = any, Entities = string>(entity: Entities, id: string, data?: NuxtStrapiQueryParams<T>): Promise<T>;

/**
* Deletes an entry by id and returns its value.
* */
delete<T = any, Entities = string>(entity: Entities, id: string): Promise<T>;

/**
* Performs an HTTP request to GraphQL API and returns the requested data.
* */
graphql<T = any>(data: NuxtStrapiGraphQLParams): Promise<T>;

/**
* Register using local strategy. Sets the User and Token.
* */
register(data: NuxtStrapiRegistrationData): Promise<void>;

/**
* Login using local strategy. Sets the User and Token.
* */
login(data: NuxtStrapiLoginData): Promise<void>;

/**
* Send a request to the forgot-password endpoint of the server.
* */
forgotPassword(data: NuxtStrapiEmailData): Promise<void>;

/**
* Send a request to the reset-password endpoint of the server.
* */
resetPassword(data: NuxtStrapiResetPasswordData): Promise<void>;

/**
* Send an email confirmation for the login.
* */
sendEmailConfirmation(data: NuxtStrapiEmailData): Promise<void>;

/**
* Clears the user and jwt in cookies.
* */
logout(): void;

/**
* Fetch me user from /users/me route if a jwt is present in the cookies.
* Sets the jwt inside $http. Sets the User.
* */
fetchUser<T = any>(): Promise<T>;

/**
* This method fully overrides the user object, avoid using it.
* You can use the $strapi.user property to mutate single
* object properties instead of overriding it completely.
* */
setUser<T = any>(user: T): void;

/**
* Returns jwt from cookies.
* */
getToken(): string;

/**
* Sets token inside $http as a jwt Bearer.
* Store jwt in cookies.
* */
setToken(token: string): void;

/**
* Remove jwt from $http and $cookies.
* */
clearToken(): void;

$http: NuxtHTTPInstance;

$cookies: NuxtCookies;
}

declare module 'vue/types/vue' {
interface Vue {
$strapi: NuxtStrapi;
}
}

declare module 'vuex/types/index' {
interface Store<S> {
$strapi: NuxtStrapi;
}
}

declare module '@nuxt/types' {
interface NuxtAppOptions {
$strapi: NuxtStrapi;
}

interface Context {
$strapi: NuxtStrapi;
}
}
2 changes: 2 additions & 0 deletions package.json
Expand Up @@ -14,6 +14,7 @@
"lib"
],
"main": "lib/module.js",
"types": "lib/index.d.ts",
"scripts": {
"dev": "nuxt example",
"docs": "nuxt docs",
Expand All @@ -34,6 +35,7 @@
"@babel/preset-env": "^7.12.11",
"@commitlint/cli": "^11.0.0",
"@commitlint/config-conventional": "^11.0.0",
"@nuxt/types": "^2.14.12",
"@nuxtjs/eslint-config": "^5.0.0",
"@nuxtjs/module-test-utils": "latest",
"babel-eslint": "latest",
Expand Down

0 comments on commit 64f1927

Please sign in to comment.