Repository: robsontenorio/vue-api-query
Pull request #281 (2024-10-04 13:21:50)

feat(model): add method wrapper to define the api response wrapper / overrides
## Checklist

- [x] Tests
- [x] Docs
- [x] Type definitions 

**Changes:**
- Added wrap, _unwrap, wrappedBy, and nowrap methods to Model.js to mimic Laravel's resource data wrapping. This makes for `$first`, `$find`, `$get`, and `$all` obsolete but I kept them for backward compatibility. The the overloadable `wrap()` allows you to automatically unwrap using the standard `first`, `find`, `get`, `all` methods but also change what the wrapper key is in case you don't want to use the standard 'data' property
- Lock the defu package to 6.1.2. Version 6.1.3+ has a regression that prevents exported objects from merging with plain objects.
- Added unit tests for the new wrap() method
- Added new `wrap()` type
- Added new `wrappedBy(wrap)` and `nowrap()` builder methods to change / disable wrapping
- Updated documentation for new `wrap()` type
- Updated documentation for new `wrappedBy()` and `nowrap()` methods

JRogaishio: I added `.nowrap()` and `wrappedBy(v)` methods since sometimes (Like for paginated responses) you want to disable wrapping at runtime for a single response.
robsontenorio: cc @JoaoPedroAS51 
HashGateApp: @robsontenorio Who should be the reviewer of this PR?
JoaoPedroAS51: Hi guys, sorry for the delay. I've been very busy lately. I'll review the PR.

@JRogaishio thank you for the PR! :)
robsontenorio: @JRogaishio  On sidebar you need to allow edit/commits by maintainers.

So @JoaoPedroAS51  can go ahead with this.

![image](https://github.com/user-attachments/assets/1fc0a01b-1578-49ef-b4d9-198a25915685)

JRogaishio: @robsontenorio I don't think I can give maintainer access to my fork / pr since it's on an organization and not **user owned**. The option just doesn't exist on the PR (see below screenshot). I did however resolve the merge conflicts and brought yarn.lock back since it appears you're using it.
![image](https://github.com/user-attachments/assets/20ff77ee-b169-4ee1-b794-f5d56a7c1f99)

Docs I'm trying to follow:
https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork

> **On user-owned forks,** if you want to allow anyone with push access to the upstream repository to make changes to your pull request, select Allow edits from maintainers.
JoaoPedroAS51: Hi @JRogaishio! Thank you for fixing the conflict! Yeah, we only save the `yarn.lock` in the repo, which is the package manager we use for this project. Can you just revert the revert the `.gitignore`? Then I think we are good to go. Many thanks! :)
JRogaishio: @JoaoPedroAS51 You're welcome. Also the .gitignore has been reverted back to the robsontenorio/dev version so it should be all set now.

Patch:
diff --git a/docs/content/en/api/model-options.md b/docs/content/en/api/model-options.md
index 8fa5179..2d479b7 100644
--- a/docs/content/en/api/model-options.md
+++ b/docs/content/en/api/model-options.md
@@ -7,10 +7,11 @@ category: API
 
 ## Global Options
 
-It's recommended to define the global options in your [Base Model](/configuration#creating-a-base-model), 
+It's recommended to define the global options in your [Base Model](/configuration#creating-a-base-model),
 in order to abstract configuration from your models.
 
 ### `$http`
+
 - Returns: `HTTP Client Instance`
 
 Instance of the HTTP client which is used to make requests.
@@ -18,6 +19,7 @@ Instance of the HTTP client which is used to make requests.
 See [Installation](/installation)
 
 ### `baseURL`
+
 - Returns: `string`
 
 Base URL which is used and prepended to make requests.
@@ -31,6 +33,7 @@ baseURL() {
 ```
 
 ### `request`
+
 - Arguments: `(config)`
 - Returns: `HTTP Client Request`
 
@@ -45,6 +48,7 @@ request(config) {
 ```
 
 ### `parameterNames`
+
 - Returns: `object`
 
 This method can be overridden in the model to customize the name of the query parameters.
@@ -66,34 +70,42 @@ parameterNames() {
 ```
 
 #### `include`
+
 - Default: `include`
 - Returns: `string`
 
 #### `filter`
+
 - Default: `filter`
 - Returns: `string`
 
 #### `sort`
+
 - Default: `sort`
 - Returns: `string`
 
 #### `fields`
+
 - Default: `fields`
 - Returns: `string`
 
 #### `append`
+
 - Default: `append`
 - Returns: `string`
 
 #### `page`
+
 - Default: `page`
 - Returns: `string`
 
 #### `limit`
+
 - Default: `limit`
 - Returns: `string`
 
 ### `formData`
+
 - Returns: `object`
 
 This method can be overridden in the model to configure `object-to-formdata`.
@@ -112,6 +124,7 @@ formData() {
 ```
 
 ### `stringifyOptions`
+
 - Default: `{ encode: false, arrayFormat: 'comma' }`
 - Returns: `object`
 
@@ -133,6 +146,7 @@ stringifyOptions() {
 These are model-related options.
 
 ### `resource`
+
 - Returns: `string`
 
 Resource route of the model which is used to build the query.
@@ -146,6 +160,7 @@ resource() {
 ```
 
 ### `primaryKey`
+
 - Default: `id`
 - Returns: `string`
 
@@ -159,10 +174,26 @@ primaryKey() {
 }
 ```
 
+### `wrap`
+
+- Default: `null`
+- Returns: `string`
+
+The "data" wrapper that should be checked when retrieving models.
+
+See [Configuration](/configuration#changing-the-wrapper)
+
+```js
+wrap() {
+  return 'data'
+}
+```
+
 ### `relations`
+
 - Returns: `object`
 
-This method can be implemented in the model to apply model instances to eager loaded relationships. 
+This method can be implemented in the model to apply model instances to eager loaded relationships.
 It works for collections too.
 
 It must return an object, which the key is the property of the relationship, and the value is the
@@ -179,6 +210,7 @@ relations() {
 ```
 
 ### `hasMany`
+
 - Arguments: `(model)`
 - Returns: `Model`
 
diff --git a/docs/content/en/api/query-builder-methods.md b/docs/content/en/api/query-builder-methods.md
index 2dbaa6d..193bd22 100644
--- a/docs/content/en/api/query-builder-methods.md
+++ b/docs/content/en/api/query-builder-methods.md
@@ -6,6 +6,7 @@ category: API
 ---
 
 ## `include`
+
 - Arguments: `(...args)`
 - Returns: `self`
 
@@ -26,6 +27,7 @@ await Model.include(['user', 'category'])
 <alert type="info">`with` is an alias of this method.</alert>
 
 ## `append`
+
 - Arguments: `(...args)`
 - Returns: `self`
 
@@ -44,17 +46,20 @@ await Model.append(['likes', 'shares'])
 ```
 
 ## `select`
+
 - Arguments: `(...fields)`
 - Returns: `self`
 
 Set the columns to be selected.
 
 #### Single entity
+
 ```js
 await Model.select(['title', 'content'])
 ```
 
 #### Related entities
+
 ```js
 await Post.select({
   posts: ['title', 'content'],
@@ -63,6 +68,7 @@ await Post.select({
 ```
 
 ## `where`
+
 - Arguments: `(field, value)`
 - Returns: `self`
 
@@ -89,6 +95,7 @@ await Model.where({ user: { status: 'active' } })
 ```
 
 ## `whereIn`
+
 - Arguments: `(field, array)`
 - Returns: `self`
 
@@ -115,13 +122,14 @@ await Model.where({ user: { id: [1, 2, 3] } })
 ```
 
 ## `orderBy`
+
 - Arguments: `(...args)`
 - Returns: `self`
 
 Add an "order by" clause to the query.
 
 ```js
-await Model.orderBy('-created_at', 'category_id')  
+await Model.orderBy('-created_at', 'category_id')
 ```
 
 #### Array
@@ -129,10 +137,11 @@ await Model.orderBy('-created_at', 'category_id')
 <alert type="success">Available in version >= v1.8.0</alert>
 
 ```js
-await Model.orderBy(['-created_at', 'category_id'])  
+await Model.orderBy(['-created_at', 'category_id'])
 ```
 
 ## `page`
+
 - Arguments: `(value)`
 - Returns: `self`
 
@@ -143,6 +152,7 @@ await Model.page(1)
 ```
 
 ## `limit`
+
 - Arguments: `(value)`
 - Returns: `self`
 
@@ -153,6 +163,7 @@ await Model.limit(20)
 ```
 
 ## `params`
+
 - Arguments: `(payload)`
 - Returns: `self`
 
@@ -161,24 +172,25 @@ Add custom parameters to the query.
 <code-group>
   <code-block Label="Query" active>
 
-  ```js
-  await Model.params({
-    foo: 'bar',
-    baz: true
-  })
-  ```
+```js
+await Model.params({
+  foo: 'bar',
+  baz: true
+})
+```
 
   </code-block>
   <code-block Label="Request">
 
-  ```http request
-  GET /resource?foo=bar&baz=true
-  ```
+```http request
+GET /resource?foo=bar&baz=true
+```
 
   </code-block>
 </code-group>
 
 ## `when`
+
 <alert type="success">Available in version >= v1.10.0</alert>
 
 - Arguments: `(value, callback)`
@@ -192,7 +204,33 @@ const search = 'foo'
 await Model.when(search, (query, value) => query.where('search', value))
 ```
 
+## `wrappedBy`
+
+<alert type="success">Available in version >= v1.12.0</alert>
+
+- Arguments: `(value)`
+- Returns: `self`
+
+Change the `wrap()` data wrapper for this request.
+
+```js
+await Model.wrappedBy('somewrapper').get()
+```
+
+## `nowrap`
+
+<alert type="success">Available in version >= v1.12.0</alert>
+
+- Returns: `self`
+
+Remove the `wrap()` data wrapper for this request to return the raw response.
+
+```js
+await Model.nowrap().get()
+```
+
 ## `custom`
+
 - Arguments: `(...args)`
 - Returns: `self`
 
@@ -201,38 +239,39 @@ Build custom endpoints.
 <code-group>
   <code-block Label="Simple Query" active>
 
-  ```js
-  await Post.custom('posts/latest')
-  ```
+```js
+await Post.custom('posts/latest')
+```
 
   </code-block>
   <code-block Label="Simple Request">
 
-  ```http request
-  GET /posts/latest
-  ```
+```http request
+GET /posts/latest
+```
 
   </code-block>
   <code-block Label="Complex Query">
 
-  ```js
-  const user = new User({ id: 1 })
-  const post = new Post()
+```js
+const user = new User({ id: 1 })
+const post = new Post()
 
-  await Post.custom(user, post, 'latest')
-  ```
+await Post.custom(user, post, 'latest')
+```
 
   </code-block>
   <code-block Label="Complex Request">
 
-  ```http request
-  GET /users/1/posts/latest
-  ```
+```http request
+GET /users/1/posts/latest
+```
 
   </code-block>
 </code-group>
 
 ## `config`
+
 <alert type="success">Available in version >= v1.8.0</alert>
 
 - Arguments: `(config)`
@@ -250,6 +289,7 @@ await Model.config({
 ```
 
 ## `get`
+
 - Returns: `Collection | { data: Collection }`
 
 Execute the query and get all results.
@@ -261,6 +301,7 @@ await Model.get()
 <alert type="info">`all` is an alias of this method.</alert>
 
 ## `first`
+
 - Returns: `Model | { data: Model }`
 
 Execute the query and get the first result.
@@ -270,6 +311,7 @@ await Model.first()
 ```
 
 ## `find`
+
 - Arguments: `(identifier)`
 - Returns: `Model | { data: Model }`
 
@@ -280,6 +322,7 @@ await Model.find(1)
 ```
 
 ## `$get`
+
 - Returns: `Collection`
 
 Execute the query and get all results.
@@ -294,6 +337,7 @@ They handle and unwrap responses within "data".</alert>
 <alert type="info">`$all` is an alias of this method.</alert>
 
 ## `$first`
+
 - Returns: `Model`
 
 Execute the query and get the first result.
@@ -302,10 +346,11 @@ Execute the query and get the first result.
 await Model.$first()
 ```
 
-<alert type="info">These `$`-prefixed convenience methods always return the requested content. 
+<alert type="info">These `$`-prefixed convenience methods always return the requested content.
 They handle and unwrap responses within "data".</alert>
 
 ## `$find`
+
 - Arguments: `(identifier)`
 - Returns: `Model`
 
@@ -315,5 +360,5 @@ Find a model by its primary key.
 await Model.$find(1)
 ```
 
-<alert type="info">These `$`-prefixed convenience methods always return the requested content. 
+<alert type="info">These `$`-prefixed convenience methods always return the requested content.
 They handle and unwrap responses within "data".</alert>
diff --git a/docs/content/en/configuration.md b/docs/content/en/configuration.md
index 68e138b..c9eb4cd 100644
--- a/docs/content/en/configuration.md
+++ b/docs/content/en/configuration.md
@@ -9,11 +9,12 @@ category: Getting Started
 
 See the [API reference](/api/model-options) for a list of available options.
 
-The first step is to create a base model to define the default options, in order to abstract configuration 
-from your models. It should extend the 
+The first step is to create a base model to define the default options, in order to abstract configuration
+from your models. It should extend the
 [Base Model](https://github.com/robsontenorio/vue-api-query/blob/master/src/Model.js) of [vue-api-query](https://github.com/robsontenorio/vue-api-query).
 
 The base model must implement two methods:
+
 - `baseURL` - The base url of your REST API.
 - `request` - The default request method.
 
@@ -43,9 +44,11 @@ export default class Model extends BaseModel {
 Now let's create our domain models that extends the base model. We can create as many models as we like.
 
 Each model must implement:
+
 - `resource` - The resource route of the model.
 
 We can create a **User** model like this:
+
 ```js{}[~/models/User.js]
 import Model from './Model'
 
@@ -71,7 +74,7 @@ export default class User extends Model {
   }
 
   // Computed properties are reactive -> user.fullName
-  // Make sure to use "get" prefix 
+  // Make sure to use "get" prefix
   get fullName () {
     return `${this.firstname} ${this.lastname}`
   }
@@ -90,6 +93,7 @@ export default class User extends Model {
 If we are working on a Typescript project, we can infer the types of the fields, so we have intellisense.
 
 #### Directly in Model
+
 ```ts{}[~/models/User.ts]
 import Model from './Model'
 
@@ -103,6 +107,7 @@ export default class User extends Model {
 ```
 
 #### Using an Interface
+
 ```ts{}[~/models/User.ts]
 import Model from './Model'
 
@@ -149,6 +154,36 @@ export default class Post extends Model {
 
 This **Post** model will build the query using the `slug` as primary key: `/posts/{slug}`
 
+## Changing the Wrapper
+
+<alert type="success">Available in version >= v1.12.0</alert>
+
+<alert type="info">By default, the `wrap` is set to `null`.</alert>
+
+See the [API reference](/api/model-options#wrap)
+
+It's possible to change the wrapper of a model response by implementing the `wrap` method.
+This way, the specified key will be used to unwrap the data.
+
+Let's create a **Post** model and set its wrap key to `data`.
+
+```js{}[~/models/Post.js]
+import Model from './Model'
+
+export default class Post extends Model {
+  // Set the resource route of the model
+  resource() {
+    return 'posts'
+  }
+
+  // Define the response wrapper
+  wrap() {
+    return 'data'
+  }
+```
+
+This **Post** model will now look inside the API response top level `data` property when retrieving data and initializing your models.
+
 ## Defining Relationships
 
 It's also possible to define the relationships of our models. By doing this, model instances will be automatically
@@ -158,7 +193,7 @@ applied to relationships, giving you access to all of their features.
 
 See the [API reference](/api/model-options#relations)
 
-For relationships that have been eager loaded, we only need to implement the `relations` method 
+For relationships that have been eager loaded, we only need to implement the `relations` method
 to apply their model instances. It works for collections too.
 
 The `relations` method must return an object, which the key is the property of the relationship, and the value is the
@@ -191,7 +226,7 @@ export default class Post extends Model {
   }
 ```
 
-Now we can easily access an instance of the **User** model containing the eager loaded data 
+Now we can easily access an instance of the **User** model containing the eager loaded data
 using the specified key: `post.user`
 
 The `relations` method also support nested keys, by dot notation:
@@ -247,7 +282,7 @@ export default class User extends Model {
   }
 
   // Computed properties are reactive -> user.fullName
-  // Make sure to use "get" prefix 
+  // Make sure to use "get" prefix
   get fullName () {
     return `${this.firstname} ${this.lastname}`
   }
@@ -290,7 +325,7 @@ export default class Model extends BaseModel {
     const customParams = {
       include: 'include_custom'
     }
-    
+
     return { ...defaultParams, ...customParams }
   }
 }
@@ -300,10 +335,10 @@ export default class Model extends BaseModel {
 
 See the [API reference](/api/model-options#stringifyOptions) and [qs](https://github.com/ljharb/qs#stringifying)
 
-We may also need to configure the parser to match our needs. By default, it is configured to match 
+We may also need to configure the parser to match our needs. By default, it is configured to match
 `spatie/laravel-query-builder`, which uses `comma` array format.
 
-If we want, for example, to change this behaviour to `indices`, we can configure the stringify options of `qs` 
+If we want, for example, to change this behaviour to `indices`, we can configure the stringify options of `qs`
 by overriding the `stringifyOptions` method.
 
 We can globally configure this in the [Base Model](/configuration#creating-a-base-model):
@@ -329,10 +364,10 @@ export default class Model extends BaseModel {
     const customParams = {
       include: 'include_custom'
     }
-    
+
     return { ...defaultParams, ...customParams }
   }
-  
+
   // Configure qs
   stringifyOptions() {
     return {
@@ -373,17 +408,17 @@ export default class Model extends BaseModel {
     const customParams = {
       include: 'include_custom'
     }
-    
+
     return { ...defaultParams, ...customParams }
   }
-  
+
   // Configure qs
   stringifyOptions() {
     return {
       arrayFormat: 'indices'
     }
   }
-  
+
   // Configure object-to-formadata
   formData() {
     return {
diff --git a/index.d.ts b/index.d.ts
index c3e34b9..4eb47da 100644
--- a/index.d.ts
+++ b/index.d.ts
@@ -368,6 +368,14 @@ export class Model extends StaticModel {
    */
   primaryKey (): string
 
+  /**
+   * The "data" wrapper that should be checked when retrieving models
+   *
+   * @see {@link https://robsontenorio.github.io/vue-api-query/api/model-options#wrap|API Reference}
+   * @see {@link https://robsontenorio.github.io/vue-api-query/configuration#changing-the-wrapper|Configuration}
+   */
+  wrap (): string
+
   /**
    * This method can be used to lazy load relationships of a model and apply model instances to them.
    *
@@ -917,6 +925,20 @@ declare class Builder {
    */
   limit (number: number): this
 
+  /**
+   * Change the `wrap()` data wrapper for this request.
+   *
+   * @see {@link https://robsontenorio.github.io/vue-api-query/api/query-builder-methods#wrappedBy|API Reference}
+   */
+  wrappedBy (attributes: string[]): this
+
+  /**
+   * Remove the `wrap()` data wrapper for this request to return the raw response.
+   *
+   * @see {@link https://robsontenorio.github.io/vue-api-query/api/query-builder-methods#nowrap|API Reference}
+   */
+  nowrap (attributes: string[]): this
+
   /**
    * Add custom parameters to the query.
    *
diff --git a/src/Model.js b/src/Model.js
index 533990f..f1eda56 100644
--- a/src/Model.js
+++ b/src/Model.js
@@ -12,6 +12,8 @@ export default class Model extends StaticModel {
 
     if (attributes.length === 0) {
       this._builder = new Builder(this)
+      // Set the default data wrapper
+      this._wrapper = this.wrap()
     } else {
       Object.assign(this, ...attributes)
       this._applyRelations(this)
@@ -56,6 +58,28 @@ export default class Model extends StaticModel {
     return 'id'
   }
 
+  /**
+   * The "data" wrapper that should be checked when retrieving models
+   *
+   * @return {string|null}
+   */
+  wrap() {
+    return null
+  }
+
+  /**
+   * Unwrap the response using the property defined in the wrap() method
+   *
+   * @return {object|array} The unwraped response
+   */
+  _unwrap(response) {
+    if (this._wrapper) {
+      return response[this._wrapper] || response
+    } else {
+      return response
+    }
+  }
+
   getPrimaryKey() {
     return this[this.primaryKey()]
   }
@@ -262,6 +286,26 @@ export default class Model extends StaticModel {
     return this
   }
 
+  /**
+   * The "data" wrapper that will override the wrap() method
+   *
+   * @param {string} wrap The new wrapper for this one request
+   *
+   * @return {string|null}
+   */
+  wrappedBy(wrap) {
+    this._wrapper = wrap
+    return this
+  }
+
+  /**
+   * Disable wrapping for this one request
+   */
+  nowrap() {
+    this._wrapper = null
+    return this
+  }
+
   /**
    * Result
    */
@@ -365,7 +409,7 @@ export default class Model extends StaticModel {
         item = response[0]
       }
 
-      return item || {}
+      return this._unwrap(item || {})
     })
   }
 
@@ -386,7 +430,7 @@ export default class Model extends StaticModel {
         method: 'GET'
       })
     ).then((response) => {
-      return this._applyInstance(response.data)
+      return this._applyInstance(this._unwrap(response.data))
     })
   }
 
@@ -421,7 +465,7 @@ export default class Model extends StaticModel {
         response.data = collection
       }
 
-      return response.data
+      return this._unwrap(response.data)
     })
   }
 
diff --git a/src/StaticModel.js b/src/StaticModel.js
index 61a25db..da2caec 100644
--- a/src/StaticModel.js
+++ b/src/StaticModel.js
@@ -88,6 +88,20 @@ export default class StaticModel {
     return self
   }
 
+  static wrappedBy(value) {
+    let self = this.instance()
+    self.wrappedBy(value)
+
+    return self
+  }
+
+  static nowrap() {
+    let self = this.instance()
+    self.nowrap()
+
+    return self
+  }
+
   static custom(...args) {
     let self = this.instance()
     self.custom(...args)


Test patch:
diff --git a/tests/model.test.js b/tests/model.test.js
index 8538181..72e8198 100644
--- a/tests/model.test.js
+++ b/tests/model.test.js
@@ -55,7 +55,7 @@ describe('Model methods', () => {
     })
   })
 
-  test('$first() returns first object in array as instance of such Model', async () => {
+  test('$first() returns first object in array as instance of such Model with "data" wrapper', async () => {
     axiosMock.onGet('http://localhost/posts').reply(200, postsEmbedResponse)
 
     const post = await Post.$first()
@@ -285,6 +285,83 @@ describe('Model methods', () => {
     expect(postsAll).toStrictEqual(postsGet)
   })
 
+  test('find() handles request with "data" wrapper when wrap() is set to "data"', async () => {
+    // Set the wrap method to 'data'
+    Post.prototype['wrap'] = () => {
+      return 'data'
+    }
+
+    axiosMock.onGet('http://localhost/posts/1').reply(200, postEmbedResponse)
+
+    const post = await Post.find(1)
+
+    expect(post).toEqual(postEmbedResponse.data)
+    expect(post).toBeInstanceOf(Post)
+    expect(post.user).toBeInstanceOf(User)
+    post.relationships.tags.data.forEach((tag) => {
+      expect(tag).toBeInstanceOf(Tag)
+    })
+  })
+
+  test('get() handles request with "data" wrapper when wrap() is set to "data"', async () => {
+    // Set the wrap method to 'data'
+    Post.prototype['wrap'] = () => {
+      return 'data'
+    }
+
+    axiosMock.onGet('http://localhost/posts').reply(200, postsEmbedResponse)
+
+    const posts = await Post.get()
+
+    expect(posts).toEqual(postsEmbedResponse.data)
+  })
+
+  test('first() returns first object in array as instance of such Model with "data" wrapper when wrap() is set to "data"', async () => {
+    // Set the wrap method to 'data'
+    Post.prototype['wrap'] = () => {
+      return 'data'
+    }
+
+    axiosMock.onGet('http://localhost/posts').reply(200, postsEmbedResponse)
+
+    const post = await Post.first()
+
+    expect(post).toEqual(postsEmbedResponse.data[0])
+    expect(post).toBeInstanceOf(Post)
+    expect(post.user).toBeInstanceOf(User)
+    post.relationships.tags.forEach((tag) => {
+      expect(tag).toBeInstanceOf(Tag)
+    })
+  })
+
+  test('nowrap().get() returns the full response with the "data" wrapper even though the wrap method is set', async () => {
+    // Set the wrap method to 'data'
+    Post.prototype['wrap'] = () => {
+      return 'data'
+    }
+
+    axiosMock.onGet('http://localhost/posts').reply(200, postsEmbedResponse)
+
+    const posts = await Post.nowrap().get()
+
+    expect(posts).toEqual(postsEmbedResponse)
+    expect(posts.data[0]).toEqual(postsEmbedResponse.data[0])
+  })
+
+  test('wrappedBy().get() returns the full response with the "data" wrapper even though the wrap method is set to `test`', async () => {
+    // Set the wrap method to 'data'
+    Post.prototype['wrap'] = () => {
+      return 'test'
+    }
+
+    axiosMock.onGet('http://localhost/posts').reply(200, postsEmbedResponse)
+
+    const posts = await Post.wrappedBy('data').get()
+
+    expect(posts).toEqual(postsEmbedResponse.data)
+    expect(posts[0]).toEqual(postsEmbedResponse.data[0])
+  })
+
   test('save() method makes a POST request when ID of object does not exists', async () => {
     let post
     const _postResponse = {