chore(i18n,learn): processed translations (#44851)
This commit is contained in:
@@ -0,0 +1,611 @@
|
||||
---
|
||||
id: 5a8b073d06fa14fcfde687aa
|
||||
title: エクササイズ記録アプリ
|
||||
challengeType: 4
|
||||
forumTopicId: 301505
|
||||
dashedName: exercise-tracker
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
<https://exercise-tracker.freecodecamp.rocks/> と同様の機能を持つフルスタック JavaScript アプリを構築してください。 プロジェクトに取り組むにあたり、以下の方法のうち 1 つを用いてコードを記述します。
|
||||
|
||||
- [ GitHub リポジトリ](https://github.com/freeCodeCamp/boilerplate-project-exercisetracker/)をクローンし、ローカル環境でプロジェクトを完了させる。
|
||||
- [Replit 始動プロジェクト](https://replit.com/github/freeCodeCamp/boilerplate-project-exercisetracker)を使用して、プロジェクトを完了させる。
|
||||
- 使い慣れたサイトビルダーを使用してプロジェクトを完了させる。 必ず GitHub リポジトリのすべてのファイルを取り込む。
|
||||
|
||||
完了したら、プロジェクトの動作デモをどこか公開の場にホストしてください。 そして、`Solution Link` フィールドでデモへの URL を送信してください。 必要に応じて、`GitHub Link` フィールドでプロジェクトのソースコードへのリンクを送信してください。
|
||||
|
||||
# --instructions--
|
||||
|
||||
レスポンスには、以下の構造体が必要です。
|
||||
|
||||
演習:
|
||||
|
||||
```js
|
||||
{
|
||||
username: "fcc_test"
|
||||
description: "test",
|
||||
duration: 60,
|
||||
date: "Mon Jan 01 1990",
|
||||
_id: "5fb5853f734231456ccb3b05"
|
||||
}
|
||||
```
|
||||
|
||||
ユーザー:
|
||||
|
||||
```js
|
||||
{
|
||||
username: "fcc_test",
|
||||
_id: "5fb5853f734231456ccb3b05"
|
||||
}
|
||||
```
|
||||
|
||||
ログ:
|
||||
|
||||
```js
|
||||
{
|
||||
username: "fcc_test",
|
||||
count: 1,
|
||||
_id: "5fb5853f734231456ccb3b05",
|
||||
log: [{
|
||||
description: "test",
|
||||
duration: 60,
|
||||
date: "Mon Jan 01 1990",
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
** ヒント: ** `date` プロパティについては、`Date` API の `toDateString` メソッド を使用すると期待した出力が得られます。
|
||||
|
||||
# --hints--
|
||||
|
||||
サンプルの URL ではなく、自分で作成したプロジェクトを提供する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) => {
|
||||
const url = getUserInput('url');
|
||||
assert(
|
||||
!/.*\/exercise-tracker\.freecodecamp\.rocks/.test(getUserInput('url'))
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
フォームデータ `username` を使用して `/api/users` への `POST` を実行することで、新しいユーザーを作成することができます。
|
||||
|
||||
```js
|
||||
async (getUserInput) => {
|
||||
const url = getUserInput('url');
|
||||
const res = await fetch(url + '/api/users', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
body: `username=fcc_test_${Date.now()}`.substr(0, 29)
|
||||
});
|
||||
assert.isTrue(res.ok);
|
||||
if(!res.ok) {
|
||||
throw new Error(`${res.status} ${res.statusText}`)
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
フォームデータ `username` による `POST /api/users` から返されるレスポンスは、`username` および `_id` プロパティを持つオブジェクトです。
|
||||
|
||||
```js
|
||||
async (getUserInput) => {
|
||||
const url = getUserInput('url');
|
||||
const res = await fetch(url + '/api/users', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
body: `username=fcc_test_${Date.now()}`.substr(0, 29)
|
||||
});
|
||||
if (res.ok) {
|
||||
const { _id, username } = await res.json();
|
||||
assert.exists(_id);
|
||||
assert.exists(username);
|
||||
} else {
|
||||
throw new Error(`${res.status} ${res.statusText}`);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
`/api/users` への `GET` リクエストを実行することにより、すべてのユーザーのリストを取得できます。
|
||||
|
||||
```js
|
||||
async(getUserInput) => {
|
||||
const url = getUserInput('url');
|
||||
const res = await fetch(url + '/api/users');
|
||||
assert.isTrue(res.ok);
|
||||
if(!res.ok) {
|
||||
throw new Error(`${res.status} ${res.statusText}`)
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
`/api/users` への `GET` リクエストを実行すると、配列が返されます。
|
||||
|
||||
```js
|
||||
async(getUserInput) => {
|
||||
const url = getUserInput('url');
|
||||
const res = await fetch(url + '/api/users');
|
||||
if(res.ok){
|
||||
const users = await res.json();
|
||||
assert.isArray(users);
|
||||
} else {
|
||||
throw new Error(`${res.status} ${res.statusText}`);
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
`GET /api/users` から返される配列の各要素は、ユーザーの `username` および `_id` を含むオブジェクトリテラルです。
|
||||
|
||||
```js
|
||||
async(getUserInput) => {
|
||||
const url = getUserInput('url');
|
||||
const res = await fetch(url + '/api/users');
|
||||
if(res.ok){
|
||||
const users = await res.json();
|
||||
const user = users[0];
|
||||
assert.exists(user);
|
||||
assert.exists(user.username);
|
||||
assert.exists(user._id);
|
||||
assert.isString(user.username);
|
||||
assert.isString(user._id);
|
||||
} else {
|
||||
throw new Error(`${res.status} ${res.statusText}`);
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
`/api/users/:_id/exercises` への `POST` では、フォームデータ `description`、`duration`、および `date` (省略可) を指定できます。 日付を指定しない場合は、現在の日付が使用されます。
|
||||
|
||||
```js
|
||||
async (getUserInput) => {
|
||||
const url = getUserInput('url');
|
||||
const res = await fetch(url + '/api/users', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
body: `username=fcc_test_${Date.now()}`.substr(0, 29)
|
||||
});
|
||||
if (res.ok) {
|
||||
const { _id, username } = await res.json();
|
||||
const expected = {
|
||||
username,
|
||||
description: 'test',
|
||||
duration: 60,
|
||||
_id,
|
||||
date: 'Mon Jan 01 1990'
|
||||
};
|
||||
const addRes = await fetch(url + `/api/users/${_id}/exercises`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
body: `description=${expected.description}&duration=${expected.duration}&date=1990-01-01`
|
||||
});
|
||||
assert.isTrue(addRes.ok);
|
||||
if(!addRes.ok) {
|
||||
throw new Error(`${addRes.status} ${addRes.statusText}`)
|
||||
};
|
||||
} else {
|
||||
throw new Error(`${res.status} ${res.statusText}`);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
`POST /api/users/:_id/exercises` から返されるレスポンスは、追加された演習フィールドを持つユーザーオブジェクトです。
|
||||
|
||||
```js
|
||||
async (getUserInput) => {
|
||||
const url = getUserInput('url');
|
||||
const res = await fetch(url + '/api/users', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
body: `username=fcc_test_${Date.now()}`.substr(0, 29)
|
||||
});
|
||||
if (res.ok) {
|
||||
const { _id, username } = await res.json();
|
||||
const expected = {
|
||||
username,
|
||||
description: 'test',
|
||||
duration: 60,
|
||||
_id,
|
||||
date: 'Mon Jan 01 1990'
|
||||
};
|
||||
const addRes = await fetch(url + `/api/users/${_id}/exercises`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
body: `description=${expected.description}&duration=${expected.duration}&date=1990-01-01`
|
||||
});
|
||||
if (addRes.ok) {
|
||||
const actual = await addRes.json();
|
||||
assert.deepEqual(actual, expected);
|
||||
assert.isString(actual.description);
|
||||
assert.isNumber(actual.duration);
|
||||
assert.isString(actual.date);
|
||||
} else {
|
||||
throw new Error(`${addRes.status} ${addRes.statusText}`);
|
||||
}
|
||||
} else {
|
||||
throw new Error(`${res.status} ${res.statusText}`);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
`/api/users/:_id/logs` への `GET` リクエストを実行すると、任意のユーザーのすべての演習ログを取得できます。
|
||||
|
||||
```js
|
||||
async (getUserInput) => {
|
||||
const url = getUserInput('url');
|
||||
const res = await fetch(url + '/api/users', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
body: `username=fcc_test_${Date.now()}`.substr(0, 29)
|
||||
});
|
||||
if (res.ok) {
|
||||
const { _id, username } = await res.json();
|
||||
const expected = {
|
||||
username,
|
||||
description: 'test',
|
||||
duration: 60,
|
||||
_id,
|
||||
date: new Date().toDateString()
|
||||
};
|
||||
const addRes = await fetch(url + `/api/users/${_id}/exercises`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
body: `description=${expected.description}&duration=${expected.duration}`
|
||||
});
|
||||
if (addRes.ok) {
|
||||
const logRes = await fetch(url + `/api/users/${_id}/logs`);
|
||||
assert.isTrue(logRes.ok);
|
||||
if(!logRes.ok) {
|
||||
throw new Error(`${logRes.status} ${logRes.statusText}`)
|
||||
};
|
||||
} else {
|
||||
throw new Error(`${addRes.status} ${addRes.statusText}`);
|
||||
}
|
||||
} else {
|
||||
throw new Error(`${res.status} ${res.statusText}`);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
ユーザーログのリクエスト `GET /api/users/:_id/logs` は、そのユーザーに属する演習の数を表す `count` プロパティを持つユーザーオブジェクトを返します。
|
||||
|
||||
```js
|
||||
async (getUserInput) => {
|
||||
const url = getUserInput('url');
|
||||
const res = await fetch(url + '/api/users', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
body: `username=fcc_test_${Date.now()}`.substr(0, 29)
|
||||
});
|
||||
if (res.ok) {
|
||||
const { _id, username } = await res.json();
|
||||
const expected = {
|
||||
username,
|
||||
description: 'test',
|
||||
duration: 60,
|
||||
_id,
|
||||
date: new Date().toDateString()
|
||||
};
|
||||
const addRes = await fetch(url + `/api/users/${_id}/exercises`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
body: `description=${expected.description}&duration=${expected.duration}`
|
||||
});
|
||||
if (addRes.ok) {
|
||||
const logRes = await fetch(url + `/api/users/${_id}/logs`);
|
||||
if (logRes.ok) {
|
||||
const { count } = await logRes.json();
|
||||
assert(count);
|
||||
} else {
|
||||
throw new Error(`${logRes.status} ${logRes.statusText}`);
|
||||
}
|
||||
} else {
|
||||
throw new Error(`${addRes.status} ${addRes.statusText}`);
|
||||
}
|
||||
} else {
|
||||
throw new Error(`${res.status} ${res.statusText}`);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
`/api/users/:id/logs` への `GET` リクエストは、追加されたすべての演習の `log` 配列を持つユーザーオブジェクトを返します。
|
||||
|
||||
```js
|
||||
async(getUserInput) => {
|
||||
const url = getUserInput('url');
|
||||
const res = await fetch(url + '/api/users', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
},
|
||||
body: `username=fcc_test_${Date.now()}`.substr(0, 29)
|
||||
})
|
||||
if(res.ok){
|
||||
const {_id, username} = await res.json();
|
||||
const expected = {
|
||||
username,
|
||||
description: 'test',
|
||||
duration: 60,
|
||||
_id,
|
||||
date: new Date().toDateString()
|
||||
};
|
||||
const addRes = await fetch(url + `/api/users/${_id}/exercises`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
body: `description=${expected.description}&duration=${expected.duration}`
|
||||
});
|
||||
if(addRes.ok){
|
||||
const logRes = await fetch(url + `/api/users/${_id}/logs`);
|
||||
if(logRes.ok) {
|
||||
const {log} = await logRes.json();
|
||||
assert.isArray(log);
|
||||
assert.equal(1, log.length);
|
||||
} else {
|
||||
throw new Error(`${logRes.status} ${logRes.statusText}`);
|
||||
}
|
||||
} else {
|
||||
throw new Error(`${addRes.status} ${addRes.statusText}`);
|
||||
};
|
||||
} else {
|
||||
throw new Error(`${res.status} ${res.statusText}`)
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
`GET /api/users/:id/logs` から返される `log` 配列内の各アイテムは、`description`、`duration` および `date` プロパティを持つオブジェクトです。
|
||||
|
||||
```js
|
||||
async(getUserInput) => {
|
||||
const url = getUserInput('url');
|
||||
const res = await fetch(url + `/api/users`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
},
|
||||
body: `username=fcc_test_${Date.now()}`.substr(0, 29)
|
||||
});
|
||||
if(res.ok) {
|
||||
const {_id, username} = await res.json();
|
||||
const expected = {
|
||||
username,
|
||||
description: 'test',
|
||||
duration: 60,
|
||||
_id,
|
||||
date: new Date().toDateString()
|
||||
};
|
||||
const addRes = await fetch(url + `/api/users/${_id}/exercises`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
body: `description=${expected.description}&duration=${expected.duration}`
|
||||
});
|
||||
if(addRes.ok) {
|
||||
const logRes = await fetch(url + `/api/users/${_id}/logs`);
|
||||
if(logRes.ok) {
|
||||
const {log} = await logRes.json();
|
||||
const exercise = log[0];
|
||||
assert.exists(exercise);
|
||||
assert.exists(exercise.description);
|
||||
assert.exists(exercise.duration);
|
||||
assert.exists(exercise.date);
|
||||
} else {
|
||||
throw new Error(`${logRes.status} ${logRes.statusText}`);
|
||||
};
|
||||
} else {
|
||||
throw new Error(`${addRes.status} ${addRes.statusText}`);
|
||||
};
|
||||
} else {
|
||||
throw new Error(`${res.status} ${res.statusText}`)
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
`GET /api/users/:id/logs` から返される `log` 配列内のどのオブジェクトの `description` プロパティも、文字列である必要があります。
|
||||
|
||||
```js
|
||||
async(getUserInput) => {
|
||||
const url = getUserInput('url');
|
||||
const res = await fetch(url + '/api/users/', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
body: `username=fcc_test_${Date.now()}`.substr(0,29)
|
||||
});
|
||||
if(res.ok) {
|
||||
const {_id, username} = await res.json();
|
||||
const expected = {
|
||||
username,
|
||||
description: 'test',
|
||||
duration: 60,
|
||||
_id,
|
||||
date: new Date().toDateString()
|
||||
};
|
||||
const addRes = await fetch(url + `/api/users/${_id}/exercises`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
body: `description=${expected.description}&duration=${expected.duration}`
|
||||
});
|
||||
if(addRes.ok) {
|
||||
const logRes = await fetch(url + `/api/users/${_id}/logs`);
|
||||
if(logRes.ok){
|
||||
const {log} = await logRes.json();
|
||||
const exercise = log[0];
|
||||
assert.isString(exercise.description);
|
||||
assert.equal(exercise.description, expected.description);
|
||||
} else {
|
||||
throw new Error(`${logRes.status} ${logRes.statusText}`);
|
||||
}
|
||||
} else {
|
||||
throw new Error(`${addRes.status} ${addRes.statusText}`);
|
||||
};
|
||||
} else {
|
||||
throw new Error(`${res.status} ${res.statusText}`);
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
`GET /api/users/:id/logs` から返される `log` 配列内のどのオブジェクトの `duration` プロパティも、数値である必要があります。
|
||||
|
||||
```js
|
||||
async(getUserInput) => {
|
||||
const url = getUserInput('url');
|
||||
const res = await fetch(url + '/api/users/', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
body: `username=fcc_test_${Date.now()}`.substr(0,29)
|
||||
});
|
||||
if(res.ok) {
|
||||
const {_id, username} = await res.json();
|
||||
const expected = {
|
||||
username,
|
||||
description: 'test',
|
||||
duration: 60,
|
||||
_id,
|
||||
date: new Date().toDateString()
|
||||
};
|
||||
const addRes = await fetch(url + `/api/users/${_id}/exercises`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
body: `description=${expected.description}&duration=${expected.duration}`
|
||||
});
|
||||
if(addRes.ok) {
|
||||
const logRes = await fetch(url + `/api/users/${_id}/logs`);
|
||||
if(logRes.ok){
|
||||
const {log} = await logRes.json();
|
||||
const exercise = log[0];
|
||||
assert.isNumber(exercise.duration);
|
||||
assert.equal(exercise.duration, expected.duration);
|
||||
} else {
|
||||
throw new Error(`${logRes.status} ${logRes.statusText}`);
|
||||
}
|
||||
} else {
|
||||
throw new Error(`${addRes.status} ${addRes.statusText}`);
|
||||
};
|
||||
} else {
|
||||
throw new Error(`${res.status} ${res.statusText}`);
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
`GET /api/users/:id/logs` から返される `log` 配列内のどのオブジェクトの `date` プロパティも、文字列である必要があります。 `Date` API の `dateString` 形式を使用してください。
|
||||
|
||||
```js
|
||||
async(getUserInput) => {
|
||||
const url = getUserInput('url');
|
||||
const res = await fetch(url + '/api/users/', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
body: `username=fcc_test_${Date.now()}`.substr(0,29)
|
||||
});
|
||||
if(res.ok) {
|
||||
const {_id, username} = await res.json();
|
||||
const expected = {
|
||||
username,
|
||||
description: 'test',
|
||||
duration: 60,
|
||||
_id,
|
||||
date: new Date().toDateString()
|
||||
};
|
||||
const addRes = await fetch(url + `/api/users/${_id}/exercises`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
body: `description=${expected.description}&duration=${expected.duration}`
|
||||
});
|
||||
if(addRes.ok) {
|
||||
const logRes = await fetch(url + `/api/users/${_id}/logs`);
|
||||
if(logRes.ok){
|
||||
const {log} = await logRes.json();
|
||||
const exercise = log[0];
|
||||
assert.isString(exercise.date);
|
||||
assert.equal(exercise.date, expected.date);
|
||||
} else {
|
||||
throw new Error(`${logRes.status} ${logRes.statusText}`);
|
||||
}
|
||||
} else {
|
||||
throw new Error(`${addRes.status} ${addRes.statusText}`);
|
||||
};
|
||||
} else {
|
||||
throw new Error(`${res.status} ${res.statusText}`);
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
`GET /api/users/:_id/logs` リクエストに `from`、`to` および `limit` パラメーターを追加すると、任意のユーザーについてログの対象部分を取得できます。 `from` および `to` は、`yyyy-mm-dd` 形式の日付です。 `limit` は、送信するログの数を表す整数です。
|
||||
|
||||
```js
|
||||
async (getUserInput) => {
|
||||
const url = getUserInput('url');
|
||||
const res = await fetch(url + '/api/users', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
body: `username=fcc_test_${Date.now()}`.substr(0, 29)
|
||||
});
|
||||
if (res.ok) {
|
||||
const { _id, username } = await res.json();
|
||||
const expected = {
|
||||
username,
|
||||
description: 'test',
|
||||
duration: 60,
|
||||
_id,
|
||||
date: new Date().toDateString()
|
||||
};
|
||||
const addExerciseRes = await fetch(url + `/api/users/${_id}/exercises`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
body: `description=${expected.description}&duration=${expected.duration}&date=1990-01-01`
|
||||
});
|
||||
const addExerciseTwoRes = await fetch(url + `/api/users/${_id}/exercises`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
body: `description=${expected.description}&duration=${expected.duration}&date=1990-01-02`
|
||||
});
|
||||
if (addExerciseRes.ok && addExerciseTwoRes.ok) {
|
||||
const logRes = await fetch(
|
||||
url + `/api/users/${_id}/logs?from=1989-12-31&to=1990-01-03`
|
||||
);
|
||||
if (logRes.ok) {
|
||||
const { log } = await logRes.json();
|
||||
assert.isArray(log);
|
||||
assert.equal(2, log.length);
|
||||
} else {
|
||||
throw new Error(`${logRes.status} ${logRes.statusText}`);
|
||||
}
|
||||
const limitRes = await fetch(
|
||||
url + `/api/users/${_id}/logs?limit=1`
|
||||
);
|
||||
if (limitRes.ok) {
|
||||
const { log } = await limitRes.json();
|
||||
assert.isArray(log);
|
||||
assert.equal(1, log.length);
|
||||
} else {
|
||||
throw new Error(`${limitRes.status} ${limitRes.statusText}`);
|
||||
}
|
||||
} else {
|
||||
throw new Error(`${res.status} ${res.statusText}`);
|
||||
}
|
||||
} else {
|
||||
throw new Error(`${res.status} ${res.statusText}`);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,88 @@
|
||||
---
|
||||
id: bd7158d8c443edefaeb5bd0f
|
||||
title: ファイルメタデータ マイクロサービス
|
||||
challengeType: 4
|
||||
forumTopicId: 301506
|
||||
dashedName: file-metadata-microservice
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
<https://file-metadata-microservice.freecodecamp.rocks/> と同様の機能を持つフルスタック JavaScript アプリを構築してください。 プロジェクトに取り組むにあたり、以下の方法のうち 1 つを用いてコードを記述します。
|
||||
|
||||
- [GitHub リポジトリ](https://github.com/freeCodeCamp/boilerplate-project-filemetadata/)をクローンし、ローカル環境でプロジェクトを完了させる。
|
||||
- [Replit 始動プロジェクト](https://replit.com/github/freeCodeCamp/boilerplate-project-filemetadata)を使用して、プロジェクトを完了させる。
|
||||
- 使い慣れたサイトビルダーを使用してプロジェクトを完了させる。 必ず GitHub リポジトリのすべてのファイルを取り込む。
|
||||
|
||||
完了したら、プロジェクトの動作デモをどこか公開の場にホストしてください。 そして、`Solution Link` フィールドでデモへの URL を送信してください。 必要に応じて、プロジェクトのソースコードへのリンクも、`GitHub Link` フィールドへ提出してください。
|
||||
|
||||
# --instructions--
|
||||
|
||||
** ヒント: ** `multer` npm パッケージを使用してファイルのアップロードを処理することができます。
|
||||
|
||||
# --hints--
|
||||
|
||||
サンプルの URL ではなく、自分で作成したプロジェクトを提供する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) => {
|
||||
assert(
|
||||
!/.*\/file-metadata-microservice\.freecodecamp\.rocks/.test(
|
||||
getUserInput('url')
|
||||
)
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
ファイルのアップロードを含むフォームを送信できます。
|
||||
|
||||
```js
|
||||
async (getUserInput) => {
|
||||
const site = await fetch(getUserInput('url'));
|
||||
const data = await site.text();
|
||||
const doc = new DOMParser().parseFromString(data, 'text/html');
|
||||
assert(doc.querySelector('input[type="file"]'));
|
||||
};
|
||||
```
|
||||
|
||||
フォームのファイル入力フィールドの `name` 属性を `upfile` に設定します。
|
||||
|
||||
```js
|
||||
async (getUserInput) => {
|
||||
const site = await fetch(getUserInput('url'));
|
||||
const data = await site.text();
|
||||
const doc = new DOMParser().parseFromString(data, 'text/html');
|
||||
assert(doc.querySelector('input[name="upfile"]'));
|
||||
};
|
||||
```
|
||||
|
||||
ファイルを送信すると、JSON レスポンスの中で `name`、`type` および `size` (バイト単位) を受け取ります。
|
||||
|
||||
```js
|
||||
async (getUserInput) => {
|
||||
const formData = new FormData();
|
||||
const fileData = await fetch(
|
||||
'https://cdn.freecodecamp.org/weather-icons/01d.png'
|
||||
);
|
||||
const file = await fileData.blob();
|
||||
formData.append('upfile', file, 'icon');
|
||||
const data = await fetch(getUserInput('url') + '/api/fileanalyse', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
const parsed = await data.json();
|
||||
assert.property(parsed, 'size');
|
||||
assert.equal(parsed.name, 'icon');
|
||||
assert.equal(parsed.type, 'image/png');
|
||||
};
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,77 @@
|
||||
---
|
||||
id: bd7158d8c443edefaeb5bdff
|
||||
title: リクエストヘッダーパーサー マイクロサービス
|
||||
challengeType: 4
|
||||
forumTopicId: 301507
|
||||
dashedName: request-header-parser-microservice
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
<https://request-header-parser-microservice.freecodecamp.rocks/> と同様の機能を持つフルスタック JavaScript アプリを構築してください。 プロジェクトに取り組むにあたり、以下の方法のうち 1 つを用いてコードを記述します。
|
||||
|
||||
- [GitHub リポジトリ](https://github.com/freeCodeCamp/boilerplate-project-headerparser/)をクローンし、ローカル環境でプロジェクトを完了させる。
|
||||
- [Replit 始動プロジェクト](https://replit.com/github/freeCodeCamp/boilerplate-project-headerparser)を使用して、プロジェクトを完了させる。
|
||||
- 使い慣れたサイトビルダーを使用してプロジェクトを完了させる。 必ず GitHub リポジトリのすべてのファイルを取り込む。
|
||||
|
||||
完了したら、プロジェクトの動作デモをどこか公開の場にホストしてください。 そして、`Solution Link` フィールドでデモへの URL を送信してください。 必要に応じて、プロジェクトのソースコードへのリンクを `GitHub Link` フィールドへ送信してください。
|
||||
|
||||
# --hints--
|
||||
|
||||
サンプルの URL ではなく、自分で作成したプロジェクトを提供する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) => {
|
||||
assert(
|
||||
!/.*\/request-header-parser-microservice\.freecodecamp\.rocks/.test(
|
||||
getUserInput('url')
|
||||
)
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
`/api/whoami` へのリクエストに対して、`ipaddress` キーに IP アドレスを持つ JSON オブジェクトを返す必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/api/whoami').then(
|
||||
(data) => assert(data.ipaddress && data.ipaddress.length > 0),
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
`/api/whoami` へのリクエストに対して、`language` キーに優先言語を持つ JSON オブジェクトを返す必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/api/whoami').then(
|
||||
(data) => assert(data.language && data.language.length > 0),
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
`/api/whoami` へのリクエストに対して、`software` キーにソフトウェアを持つ JSON オブジェクトを返す必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/api/whoami').then(
|
||||
(data) => assert(data.software && data.software.length > 0),
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,156 @@
|
||||
---
|
||||
id: bd7158d8c443edefaeb5bdef
|
||||
title: タイムスタンプ マイクロサービス
|
||||
challengeType: 4
|
||||
forumTopicId: 301508
|
||||
dashedName: timestamp-microservice
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
<https://timestamp-microservice.freecodecamp.rocks/> と同様の機能を持つフルスタック JavaScript アプリを構築してください。 プロジェクトに取り組むにあたり、以下の方法のうち 1 つを用いてコードを記述します。
|
||||
|
||||
- [GitHub リポジトリ](https://github.com/freeCodeCamp/boilerplate-project-timestamp/)をクローンし、ローカル環境でプロジェクトを完了させる。
|
||||
- [Replit 始動プロジェクト](https://replit.com/github/freeCodeCamp/boilerplate-project-timestamp)を使用して、プロジェクトを完了させる。
|
||||
- 使い慣れたサイトビルダーを使用してプロジェクトを完了させる。 必ず GitHub リポジトリのすべてのファイルを取り込む。
|
||||
|
||||
完了したら、プロジェクトの動作デモをどこか公開の場にホストしてください。 そして、`Solution Link` フィールドでデモへの URL を送信してください。 必要に応じて、プロジェクトのソースコードへのリンクも、`GitHub Link` フィールドへ提出してください。
|
||||
|
||||
**注:** タイムゾーンの変換は、このプロジェクトの目的ではありません。そのため、有効な日付が送信された場合、GMT の日時として `new Date()` に解釈されると想定してください。
|
||||
|
||||
# --hints--
|
||||
|
||||
サンプルの URL ではなく、自分で作成したプロジェクトを提供する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) => {
|
||||
assert(
|
||||
!/.*\/timestamp-microservice\.freecodecamp\.rocks/.test(getUserInput('url'))
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
有効な日付を持つ `/api/:date?` へのリクエストに対して、JSON オブジェクトを返し、その `unix` キーの値は入力日付を表す Unix タイムスタンプ (ミリ秒) の値である必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/api/2016-12-25').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.unix,
|
||||
1482624000000,
|
||||
'Should be a valid unix timestamp'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
有効な日付を持つ `/api/:date?` へのリクエストに対して、JSON オブジェクトを返し、その `utc` キーの値は入力日付を表す `Thu, 01 Jan 1970 00:00:00 GMT` 形式の文字列である必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/api/2016-12-25').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.utc,
|
||||
'Sun, 25 Dec 2016 00:00:00 GMT',
|
||||
'Should be a valid UTC date string'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
`/api/1451001600000` へのリクエストに対して、`{ unix: 1451001600000, utc: "Fri, 25 Dec 2015 00:00:00 GMT" }` を返す必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/api/1451001600000').then(
|
||||
(data) => {
|
||||
assert(
|
||||
data.unix === 1451001600000 &&
|
||||
data.utc === 'Fri, 25 Dec 2015 00:00:00 GMT'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
あなたのプロジェクトは、`new Date(date_string)` で正常に解析できる日付を処理することができます。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/api/05 October 2011, GMT').then(
|
||||
(data) => {
|
||||
assert(
|
||||
data.unix === 1317772800000 &&
|
||||
data.utc === 'Wed, 05 Oct 2011 00:00:00 GMT'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
入力された日付文字列が無効な場合、API は `{ error : "Invalid Date" }` という構造のオブジェクトを返します。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/api/this-is-not-a-date').then(
|
||||
(data) => {
|
||||
assert.equal(data.error.toLowerCase(), 'invalid date');
|
||||
},
|
||||
(xhr) => {
|
||||
assert(xhr.responseJSON.error.toLowerCase() === 'invalid date');
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
空の日付パラメータに対して、`unix` キーを持ち、現在時刻を表す JSON オブジェクトを返す必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/api').then(
|
||||
(data) => {
|
||||
var now = Date.now();
|
||||
assert.approximately(data.unix, now, 20000);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
空の日付パラメータに対して、`utc` キーを持ち、現在時刻を表す JSON オブジェクトを返す必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/api').then(
|
||||
(data) => {
|
||||
var now = Date.now();
|
||||
var serverTime = new Date(data.utc).getTime();
|
||||
assert.approximately(serverTime, now, 20000);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,119 @@
|
||||
---
|
||||
id: bd7158d8c443edefaeb5bd0e
|
||||
title: URL 短縮マイクロサービス
|
||||
challengeType: 4
|
||||
forumTopicId: 301509
|
||||
dashedName: url-shortener-microservice
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
<https://url-shortener-microservice.freecodecamp.rocks/> と同様の機能を持つフルスタック JavaScript アプリを構築してください。 プロジェクトに取り組むにあたり、以下の方法のうち 1 つを用いてコードを記述します。
|
||||
|
||||
- [GitHub リポジトリ](https://github.com/freeCodeCamp/boilerplate-project-urlshortener/)をクローンし、ローカル環境でプロジェクトを完了させる。
|
||||
- [Replit 始動プロジェクト](https://replit.com/github/freeCodeCamp/boilerplate-project-urlshortener)を使用して、プロジェクトを完了させる。
|
||||
- 使い慣れたサイトビルダーを使用してプロジェクトを完了させる。 必ず GitHub リポジトリのすべてのファイルを取り込む。
|
||||
|
||||
完了したら、プロジェクトの動作デモをどこか公開の場にホストしてください。 そして、`Solution Link` フィールドでデモへの URL を送信してください。 必要に応じて、プロジェクトのソースコードへのリンクも、`GitHub Link` フィールドへ提出してください。
|
||||
|
||||
# --instructions--
|
||||
|
||||
** ヒント: ** POST リクエストを処理するために、ボディ解析ミドルウェアを使用してください。 また、`dns` コアモジュールの `dns.lookup(host, cb)` 関数を使用して、送信された URL を確認することも可能です。
|
||||
|
||||
# --hints--
|
||||
|
||||
サンプルの URL ではなく、自分で作成したプロジェクトを提供する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) => {
|
||||
assert(
|
||||
!/.*\/url-shortener-microservice\.freecodecamp\.rocks/.test(
|
||||
getUserInput('url')
|
||||
)
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
`/api/shorturl` へ URL を POST し、`original_url` および `short_url` プロパティを持つ JSON レスポンスを取得できます。 例: `{ original_url : 'https://freeCodeCamp.org', short_url : 1}`
|
||||
|
||||
```js
|
||||
async (getUserInput) => {
|
||||
const url = getUserInput('url');
|
||||
const urlVariable = Date.now();
|
||||
const fullUrl = `${url}/?v=${urlVariable}`
|
||||
const res = await fetch(url + '/api/shorturl', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
body: `url=${fullUrl}`
|
||||
});
|
||||
if (res.ok) {
|
||||
const { short_url, original_url } = await res.json();
|
||||
assert.isNotNull(short_url);
|
||||
assert.strictEqual(original_url, `${url}/?v=${urlVariable}`);
|
||||
} else {
|
||||
throw new Error(`${res.status} ${res.statusText}`);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
`/api/shorturl/<short_url>` へアクセスすると、元の URL にリダイレクトされます。
|
||||
|
||||
```js
|
||||
async (getUserInput) => {
|
||||
const url = getUserInput('url');
|
||||
const urlVariable = Date.now();
|
||||
const fullUrl = `${url}/?v=${urlVariable}`
|
||||
let shortenedUrlVariable;
|
||||
const postResponse = await fetch(url + '/api/shorturl', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
body: `url=${fullUrl}`
|
||||
});
|
||||
if (postResponse.ok) {
|
||||
const { short_url } = await postResponse.json();
|
||||
shortenedUrlVariable = short_url;
|
||||
} else {
|
||||
throw new Error(`${postResponse.status} ${postResponse.statusText}`);
|
||||
}
|
||||
const getResponse = await fetch(
|
||||
url + '/api/shorturl/' + shortenedUrlVariable
|
||||
);
|
||||
if (getResponse) {
|
||||
const { redirected, url } = getResponse;
|
||||
assert.isTrue(redirected);
|
||||
assert.strictEqual(url,fullUrl);
|
||||
} else {
|
||||
throw new Error(`${getResponse.status} ${getResponse.statusText}`);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
有効な `http://www.example.com` 形式に従っていない無効な URL を渡すと、JSON レスポンスで `{ error: 'invalid url' }` が返されます。
|
||||
|
||||
```js
|
||||
async (getUserInput) => {
|
||||
const url = getUserInput('url');
|
||||
const res = await fetch(url + '/api/shorturl', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
body: `url=ftp:/john-doe.org`
|
||||
});
|
||||
if (res.ok) {
|
||||
const { error } = await res.json();
|
||||
assert.isNotNull(error);
|
||||
assert.strictEqual(error.toLowerCase(), 'invalid url');
|
||||
} else {
|
||||
throw new Error(`${res.status} ${res.statusText}`);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,79 @@
|
||||
---
|
||||
id: 587d7fb1367417b2b2512bf4
|
||||
title: ミドルウェアをチェーンしてタイムサーバーを作成する
|
||||
challengeType: 2
|
||||
forumTopicId: 301510
|
||||
dashedName: chain-middleware-to-create-a-time-server
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
`app.METHOD(path, middlewareFunction)` を使用して、ミドルウェアを特定のルートにマウントすることができます。 ミドルウェアをルート定義の中でチェーンすることもできます。
|
||||
|
||||
以下の例を見てください。
|
||||
|
||||
```js
|
||||
app.get('/user', function(req, res, next) {
|
||||
req.user = getTheUserSync(); // Hypothetical synchronous operation
|
||||
next();
|
||||
}, function(req, res) {
|
||||
res.send(req.user);
|
||||
});
|
||||
```
|
||||
|
||||
このアプローチは、サーバー操作を小さいユニットに分割するのに便利です。 これにより、アプリの構造が整理され、異なる場所でコードを再利用できるようになります。 このアプローチは、データに対して何らかの検証を実行する場合にも使用できます。 ミドルウェア スタックの各ポイントで、現在のチェーンの実行をブロックし、エラーを処理するために特別に設計された関数へ制御を渡すことができます。 または、特別なケースを処理するために、次にマッチするルートへ制御を渡すこともできます。 詳しくは「高度な Express」セクションで説明します。
|
||||
|
||||
# --instructions--
|
||||
|
||||
ルート `app.get('/now', ...)` で、ミドルウェア関数と最後のハンドラーをチェーンします。 ミドルウェア関数では、リクエストオブジェクトの `req.time` キーに現在時刻を追加する必要があります。 `new Date().toString()` を使用できます。 ハンドラーでは、JSON オブジェクトで応答し、構造体 `{time: req.time}` を受け取ってください。
|
||||
|
||||
** 注意:** ミドルウェアをチェーンしない場合、テストには合格しません。 関数を別の場所にマウントした場合、出力結果が正しくてもテストは不合格となります。
|
||||
|
||||
# --hints--
|
||||
|
||||
/now エンドポイントで、ミドルウェアをマウントしている必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/chain-middleware-time').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.stackLength,
|
||||
2,
|
||||
'"/now" route has no mounted middleware'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
/now エンドポイントで、今から +/- 20 秒の時間を返す必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/chain-middleware-time').then(
|
||||
(data) => {
|
||||
var now = new Date();
|
||||
assert.isAtMost(
|
||||
Math.abs(new Date(data.time) - now),
|
||||
20000,
|
||||
'the returned time is not between +- 20 secs from now'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,78 @@
|
||||
---
|
||||
id: 587d7fb2367417b2b2512bf8
|
||||
title: POST リクエストからデータを取得する
|
||||
challengeType: 2
|
||||
forumTopicId: 301511
|
||||
dashedName: get-data-from-post-requests
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
パス `/name` で POST ハンドラーをマウントしてください。 これは前回と同じパスです。 html のフロントページにフォームが用意されています。 このフォームは、演習 10 (クエリ文字列) と同じデータを送信します。 ボディパーサーが正しく設定されている場合は、`req.body` オブジェクトにパラメーターが見つかります。 次は通常のライブラリの例です。
|
||||
|
||||
<blockquote>route: POST '/library'<br>urlencoded_body: userId=546&bookId=6754 <br>req.body: {userId: '546', bookId: '6754'}</blockquote>
|
||||
|
||||
前回と同じ JSON オブジェクト `{name: 'firstname lastname'}` で応答してください。 アプリのフロントページに提供されている html フォームを使用してエンドポイントが動作するかどうかをテストしてください。
|
||||
|
||||
ヒント: GET と POST 以外にもいくつかの http メソッドがあります。 慣例として、http 動詞とサーバ上で実行する操作には対応関係があります。 従来の対応関係は以下のようになっています。
|
||||
|
||||
POST (場合によっては PUT) - リクエストと共に送信された情報を使用して新しいリソースを作成する。
|
||||
|
||||
GET - 既存のリソースを変更せずに読み取る。
|
||||
|
||||
PUT または PATCH (場合によっては POST) - 送信されたデータを使用してリソースを更新する。
|
||||
|
||||
DELETE => リソースを削除する。
|
||||
|
||||
他にもサーバーとの接続のやり取りに使用するメソッドがいくつかあります。 GET を除いて、上記のメソッドはすべてペイロードを持つことができます (つまり、データをリクエストボディに保存できます)。 ボディ解析ミドルウェアでもこれらのメソッドを使用できます。
|
||||
|
||||
# --hints--
|
||||
|
||||
テスト 1: API エンドポイントは、正しい名前で応答する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.post(getUserInput('url') + '/name', { first: 'Mick', last: 'Jagger' }).then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.name,
|
||||
'Mick Jagger',
|
||||
'Test 1: "POST /name" route does not behave as expected'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
テスト 2: API エンドポイントは、正しい名前で応答する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.post(getUserInput('url') + '/name', {
|
||||
first: 'Keith',
|
||||
last: 'Richards'
|
||||
}).then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.name,
|
||||
'Keith Richards',
|
||||
'Test 2: "POST /name" route does not behave as expected'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,67 @@
|
||||
---
|
||||
id: 587d7fb2367417b2b2512bf6
|
||||
title: クライアントからクエリパラメーター入力を取得する
|
||||
challengeType: 2
|
||||
forumTopicId: 301512
|
||||
dashedName: get-query-parameter-input-from-the-client
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
クライアントから入力を取得するもう一つの一般的な方法として、クエリ文字列を使用してルートパスの後にデータをエンコードすることができます。 クエリ文字列は、疑問符 (?) で区切られ、field=value のペアを含みます。 各ペアはアンパサンド (&) で区切られます。 Express では、クエリ文字列のデータを解析し、`req.query` オブジェクトを設定することができます。 パーセント (%) などのいくつかの文字は URL に含めることができないので、送信する前に別の形式にエンコードする必要があります。 JavaScript の API を使用する場合は、そうした文字をエンコード/デコードするために特定のメソッドを使用できます。
|
||||
|
||||
<blockquote>route_path: '/library'<br>actual_request_URL: '/library?userId=546&bookId=6754' <br>req.query: {userId: '546', bookId: '6754'}</blockquote>
|
||||
|
||||
# --instructions--
|
||||
|
||||
API エンドポイントを構築し、`GET /name` でマウントしてください。 JSON ドキュメントで応答し、構造体 `{ name: 'firstname lastname'}` を受け取ってください。 ファーストネームおよびラストネームのパラメーターは、`?first=firstname&last=lastname` などのクエリ文字列にエンコードする必要があります。
|
||||
|
||||
** 注: ** 以下の演習では、同じ `/name` ルートパスで、POST リクエストからデータを受け取ります。 必要に応じて、メソッド `app.route(path).get(handler).post(handler)` を使用できます。 このシンタックスにより、同じパスルート上で異なる動詞ハンドラーをチェーンすることができます。 入力する文字数が少しだけ減り、コードがわかりやすくなります。
|
||||
|
||||
# --hints--
|
||||
|
||||
テスト 1: API エンドポイントは、正しい名前で応答する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/name?first=Mick&last=Jagger').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.name,
|
||||
'Mick Jagger',
|
||||
'Test 1: "GET /name" route does not behave as expected'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
テスト 2: API エンドポイントは、正しい名前で応答する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/name?last=Richards&first=Keith').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.name,
|
||||
'Keith Richards',
|
||||
'Test 2: "GET /name" route does not behave as expected'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,65 @@
|
||||
---
|
||||
id: 587d7fb2367417b2b2512bf5
|
||||
title: クライアントからルートパラメータ入力を取得する
|
||||
challengeType: 2
|
||||
forumTopicId: 301513
|
||||
dashedName: get-route-parameter-input-from-the-client
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
API を構築する際は、私たちが作成するサービスから何を得たいのかをユーザーに伝えてもらうようにする必要があります。 たとえばクライアントが、データベースに保存されているユーザーに関する情報を要求している場合、どのユーザーに興味があるのかをクライアントから伝えてもらうための手段が必要になります。 そのための方法の一つとして、ルートパラメーターの使用があります。 ルートパラメーターは名前の付いた URL のセグメントであり、スラッシュ (/) で区切られます。 各セグメントは、URL のうちその位置と一致する部分の値を取得します。 取得した値は、`req.params` オブジェクトに保存されます。
|
||||
|
||||
<blockquote>route_path: '/user/:userId/book/:bookId'<br>actual_request_URL: '/user/546/book/6754' <br>req.params: {userId: '546', bookId: '6754'}</blockquote>
|
||||
|
||||
# --instructions--
|
||||
|
||||
エコーサーバーを構築し、ルート `GET /:word/echo` にマウントしてください。 JSON オブジェクトで応答し、構造体 `{echo: word}` を受け取ってください。 繰り返すワードは `req.params.word` で見つけることができます。 たとえば、`your-app-rootpath/freecodecamp/echo` のように、一致するルートにアクセスして、ブラウザーのアドレスバーからルートをテストすることができます。
|
||||
|
||||
# --hints--
|
||||
|
||||
テスト 1: エコーサーバーは、ワードを正しく繰り返す必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/eChOtEsT/echo').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.echo,
|
||||
'eChOtEsT',
|
||||
'Test 1: the echo server is not working as expected'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
テスト 2: エコーサーバーは、ワードを正しく繰り返す必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/ech0-t3st/echo').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.echo,
|
||||
'ech0-t3st',
|
||||
'Test 2: the echo server is not working as expected'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,57 @@
|
||||
---
|
||||
id: 587d7fb1367417b2b2512bf3
|
||||
title: ルートレベルのリクエスト ロガー ミドルウェアを実装する
|
||||
challengeType: 2
|
||||
forumTopicId: 301514
|
||||
dashedName: implement-a-root-level-request-logger-middleware
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
前に `express.static()` ミドルウェア関数について説明しました。 ここでは、ミドルウェアとはどのようなものなのか、もっと詳しく説明しましょう。 ミドルウェア関数は 3 つの引数を取る関数です。それらは、リクエストオブジェクト、レスポンスオブジェクト、そして、アプリケーションのリクエストレスポンスサイクルにおける次の関数です。 これらの関数は、アプリケーションに副次的な効果を与えるコードを実行し、通常はリクエストオブジェクトもしくはレスポンスオブジェクトへ情報を追加します。 また、条件が満たされたときにレスポンスを送信して、サイクルを終了することもできます。 終了時にレスポンスを送信しない場合は、スタック内で次の関数の実行を開始します。 これにより、3 番目の引数 `next()` が呼び出されます。
|
||||
|
||||
以下の例を見てください。
|
||||
|
||||
```js
|
||||
function(req, res, next) {
|
||||
console.log("I'm a middleware...");
|
||||
next();
|
||||
}
|
||||
```
|
||||
|
||||
ルートにこの関数をマウントしたとしましょう。 リクエストがルートと一致すると、「I'm a middleware…」という文字列が表示され、スタック内で次の関数が実行されます。 この演習では、ルートレベルのミドルウェアを構築します。 チャレンジ 4 で説明したように、ミドルウェア関数をルートレベルでマウントするには、 `app.use(<mware-function>)` メソッドを使用できます。 この場合、関数はすべてのリクエストに対して実行されますが、より具体的な条件を設定することもできます。 たとえば、POST リクエストに対してのみ関数を実行したい場合は、 `app.post(<mware-function>)` を使用できます。 すべての HTTP 動詞 (GET、DELETE、PUT、 … ) について、類似したメソッドがあります。
|
||||
|
||||
# --instructions--
|
||||
|
||||
シンプルなロガーを構築してください。 リクエストごとに、`method path - ip` という形式の文字列をコンソールへ記録する必要があります。 たとえば `GET /json - ::ffff:127.0.0.1` などとします。 `method` と `path` の間に空白を入れ、`path` と `ip` を区切るダッシュの両側を空白で囲むことに注意してください。 `req.method`、`req.path` および `req.ip` を使用して、リクエストメソッド (http 動詞)、相対ルートパスおよびリクエストオブジェクトの発信者 ip を取得できます。 処理を終えたら忘れずに `next()` を呼び出してください。そうしないとサーバーがずっと無応答になります。 必ず 「Logs」を開き、リクエストが届いたときにどうなるか確認してください。
|
||||
|
||||
** 注: ** Express では、関数はコード内に出現する順序で評価されます。 この動作はミドルウェアにも適用されます。 すべてのルートで動作させたい場合は、それらの前でマウントする必要があります。
|
||||
|
||||
# --hints--
|
||||
|
||||
ルートレベルのロガーミドルウェアをアクティブにする必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/root-middleware-logger').then(
|
||||
(data) => {
|
||||
assert.isTrue(
|
||||
data.passed,
|
||||
'root-level logger is not working as expected'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,53 @@
|
||||
---
|
||||
id: 587d7fb0367417b2b2512bed
|
||||
title: ノードコンソールを確認する
|
||||
challengeType: 2
|
||||
forumTopicId: 301515
|
||||
dashedName: meet-the-node-console
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
チャレンジに取り組むにあたり、以下の方法のうち 1 つを用いてコードを記述します。
|
||||
|
||||
- [GitHub レポジトリ](https://github.com/freeCodeCamp/boilerplate-express/)をクローンし、ローカル環境でチャレンジを完了させる。
|
||||
- [Replit 始動プロジェクト](https://replit.com/github/freeCodeCamp/boilerplate-express)を使用してチャレンジを完了させる。
|
||||
- 使い慣れたサイトビルダーを使用してプロジェクトを完了させる。 必ず GitHub リポジトリのすべてのファイルを取り込む。
|
||||
|
||||
完了したら、プロジェクトの動作デモをどこか公開の場にホストしてください。 そして、`Solution Link` フィールドでデモへの URL を送信してください。
|
||||
|
||||
開発プロセスでは、コード内で何が起きているかを確認できるようにすることが重要です。
|
||||
|
||||
Node は単なる JavaScript 環境にすぎません。 クライアントサイド JavaScript と同様に、コンソールを使用して有用なデバッグ情報を表示できます。 ローカルマシンでは、ターミナルでコンソール出力を確認できます。 Replit では、デフォルトで右側のペインにターミナルが開きます。
|
||||
|
||||
チャレンジの作業中はターミナルを開いたままにしておくことをお勧めします。 ターミナルの出力を読み取ることで、発生する可能性のあるエラーを確認することができます。
|
||||
|
||||
# --instructions--
|
||||
|
||||
`myApp.js` ファイルを変更して、コンソールログに「Hello World」と出力してください。
|
||||
|
||||
# --hints--
|
||||
|
||||
`"Hello World"` がコンソールに表示されます。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/hello-console').then(
|
||||
(data) => {
|
||||
assert.isTrue(data.passed, '"Hello World" is not in the server console');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,51 @@
|
||||
---
|
||||
id: 587d7fb0367417b2b2512bef
|
||||
title: HTML ファイルを提供する
|
||||
challengeType: 2
|
||||
forumTopicId: 301516
|
||||
dashedName: serve-an-html-file
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
`res.sendFile(path)` メソッドを使用して、ファイルでリクエストに応答することができます。 メソッドは `app.get('/', ...)` ルートハンドラーの中に記述できます。 このメソッドは、送信したいファイルの処理方法をブラウザーに指示するための適切なヘッダーを、そのファイルタイプに応じて設定します。 そして、ファイルを読み取り、送信します。 このメソッドには絶対ファイルパスが必要です。 以下のようなパスを計算するために、Node のグローバル変数 `__dirname` を使用することを推奨します。
|
||||
|
||||
```js
|
||||
absolutePath = __dirname + relativePath/file.ext
|
||||
```
|
||||
|
||||
# --instructions--
|
||||
|
||||
GET リクエストへのレスポンスとして、`/views/index.html` ファイルを `/` パスへ送信します。 動作可能なアプリを表示すると、大きな HTML 見出し (および、あとで使用するフォーム) が、スタイルが適用されていない状態で表示されることが確認できるはずです。
|
||||
|
||||
**注:** 前回のチャレンジのソリューションを編集するか、新しいチャレンジを作成できます。 新しいソリューションを作成する場合、Express ではルートが上から下に向かって評価され、最初に一致したルートのハンドラーが実行されることに注意してください。 前のソリューションをコメントアウトする必要があります。そうしないとサーバーは文字列で応答し続けます。
|
||||
|
||||
# --hints--
|
||||
|
||||
アプリからファイル views/index.html を提供する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url')).then(
|
||||
(data) => {
|
||||
assert.match(
|
||||
data,
|
||||
/<h1>.*<\/h1>/,
|
||||
'Your app does not serve the expected HTML'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,47 @@
|
||||
---
|
||||
id: 587d7fb1367417b2b2512bf1
|
||||
title: 特定のルート上で JSON を提供する
|
||||
challengeType: 2
|
||||
forumTopicId: 301517
|
||||
dashedName: serve-json-on-a-specific-route
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
HTML サーバーは HTML を提供しますが、API はデータを提供します。 <dfn>REST</dfn> (REpresentational State Transfer) API では、クライアントがサーバーの詳細を知らなくても、簡単な方法でデータ交換ができます。 クライアントで知る必要があるのは、リソースのある場所 (URL) と、そこで実行したいアクション (動詞) だけです。 GET 動詞は、情報を取得するだけで何も変更を加えない場合に使用します。 最近では、ウェブでの情報の操作に使用するデータ形式として JSON が好まれています。 簡単に言えば、JSON は JavaScript オブジェクトを文字列として表現する便利な手段であり、簡単に送信することができます。
|
||||
|
||||
シンプルな API として、パス `/json` で JSON で応答するルートを作成してみましょう。 いつものように `app.get()` メソッドを使用できます。 ルートハンドラートの中で、メソッド `res.json()` を使用し、オブジェクトを引数として渡します。 このメソッドは、リクエスト-レスポンス ループを閉じ、データを返します。 バックグラウンドでは、有効な JavaScript オブジェクトを文字列に変換します。そして、適切なヘッダーを設定して、JSON を提供しようとしていることをブラウザーに伝え、データを返します。 有効なオブジェクトには通常の構造体 `{key: data}` があります。 `data` は、数値、文字列、ネストされたオブジェクトまたは配列です。 `data` はまた、変数または関数呼び出しの結果になることもあり、その場合は文字列に変換される前に評価されます。
|
||||
|
||||
# --instructions--
|
||||
|
||||
オブジェクト `{"message": "Hello json"}` を JSON 形式のレスポンスとして、`/json` ルートへの GET リクエストに提供します。 ブラウザーから `your-app-url/json` にアクセスすると、画面にメッセージが表示されます。
|
||||
|
||||
# --hints--
|
||||
|
||||
エンドポイント `/json` は json オブジェクト `{"message": "Hello json"}` を提供する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/json').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.message,
|
||||
'Hello json',
|
||||
"The '/json' endpoint does not serve the right data"
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,51 @@
|
||||
---
|
||||
id: 587d7fb0367417b2b2512bf0
|
||||
title: 静的アセットを提供する
|
||||
challengeType: 2
|
||||
forumTopicId: 301518
|
||||
dashedName: serve-static-assets
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
HTML サーバーには通常、ユーザーがアクセスできるディレクトリが 1 つ以上あります。 それらのディレクトリには、アプリに必要な静的アセット (スタイルシート、スクリプト、画像) を置くことができます。
|
||||
|
||||
Express では、ミドルウェア `express.static(path)` を使用してこの機能を実現できます。ここで、`path` パラメーターはアセットのあるフォルダーの絶対パスです。
|
||||
|
||||
ミドルウェアのことが分からなくても心配はいりません。後で詳しく説明します。 基本的に、ミドルウェアとは、ルートハンドラーに割り込んで何らかの情報を追加する関数です。 ミドルウェアはメソッド `app.use(path, middlewareFunction)` を使用してマウントする必要があります。 最初の `path` 引数は任意です。 これを渡さない場合は、すべてのリクエストに対してミドルウェアが実行されます。
|
||||
|
||||
# --instructions--
|
||||
|
||||
`app.use()` を使用して、`express.static()` ミドルウェアをパス `/public` にマウントしてください。 アセットフォルダーへの絶対パスは `__dirname + /public` です。
|
||||
|
||||
これで、アプリが CSS スタイルシートを提供できるようになります。 `/public/style.css` ファイルは、プロジェクト ボイラープレートの `/views/index.html` で参照されることに注意してください。 これで、フロントページの見栄えが少し良くなっているはずです!
|
||||
|
||||
# --hints--
|
||||
|
||||
アプリは、`/public` ディレクトリから `/public` パスにアセットファイルを提供する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/public/style.css').then(
|
||||
(data) => {
|
||||
assert.match(
|
||||
data,
|
||||
/body\s*\{[^\}]*\}/,
|
||||
'Your app does not serve static assets'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,57 @@
|
||||
---
|
||||
id: 587d7fb0367417b2b2512bee
|
||||
title: 作業用の Express サーバーを起動する
|
||||
challengeType: 2
|
||||
forumTopicId: 301519
|
||||
dashedName: start-a-working-express-server
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
ファイル `myApp.js` の最初の 2 行を見ると、Express アプリオブジェクトを簡単に作成できることがわかります。 このオブジェクトにはいくつかのメソッドがあり、チャレンジでそれらの多くを学ぶことになります。 基本的なメソッドの一つが、`app.listen(port)` です。 このメソッドは、指定されたポートをリッスンするようにサーバーへ指示し、サーバーを実行中の状態にします。 テスト目的のため、アプリをバックグラウンドで実行する必要があります。そのため、このメソッドを `server.js` ファイルに追加しました。
|
||||
|
||||
サーバーから最初の文字列を提供してみましょう! Express では、ルートは `app.METHOD(PATH, HANDLER)` という構造をとります。 METHOD は、小文字の http メソッドです。 PATH は、サーバー上の相対パスです (文字列、または正規表現でもかまいません) 。 HANDLER は、ルートが一致したときに Express が呼び出す関数です。 ハンドラーは `function(req, res) {...}` という形式をとります。req はリクエストオブジェクトで、res はレスポンスオブジェクトです。 たとえば、下記のハンドラー
|
||||
|
||||
```js
|
||||
function(req, res) {
|
||||
res.send('Response String');
|
||||
}
|
||||
```
|
||||
|
||||
は、文字列「Response String」を提供します。
|
||||
|
||||
# --instructions--
|
||||
|
||||
`app.get()` メソッドを使用して、`/` (root) パスに一致する GET リクエストに対して文字列「Hello Express」を提供してください。 ログを見てコードが動作することを確認し、Replit を使用している場合はプレビューで結果を確認してください。
|
||||
|
||||
** 注: ** レッスンのコードはすべて、あらかじめ用意されている数行のコードの間に追加する必要があります。
|
||||
|
||||
# --hints--
|
||||
|
||||
アプリは、「Hello Express」という文字列を提供する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url')).then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data,
|
||||
'Hello Express',
|
||||
'Your app does not serve the text "Hello Express"'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,63 @@
|
||||
---
|
||||
id: 587d7fb2367417b2b2512bf7
|
||||
title: POST リクエストを解析するためにボディパーサーを使用する
|
||||
challengeType: 2
|
||||
forumTopicId: 301520
|
||||
dashedName: use-body-parser-to-parse-post-requests
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
GET のほかにもう一つ一般的な HTTP 動詞があります。それは、POST です。 POST は、HTML フォームを使用してクライアントデータを送信するために使用するデフォルトのメソッドです。 REST の慣例では、POST は、データベースに新しいアイテム (新規ユーザー、または新しいブログ投稿) を作成するためのデータを送信する場合に使用します。 このプロジェクトにはデータベースはありませんが、ひとまず POST リクエストの処理方法について説明します。
|
||||
|
||||
このようなリクエストでは、データは URL には表示されず、リクエストボディに隠されています。 ボディは HTTP リクエストの一部であり、ペイロードとも呼ばれます。 URL にデータが表示されていなくても、データが非公開であるとは限りません。 その理由を理解するため、HTTP POST リクエストの処理前の内容を見てみましょう。
|
||||
|
||||
```http
|
||||
POST /path/subpath HTTP/1.0
|
||||
From: john@example.com
|
||||
User-Agent: someBrowser/1.0
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
Content-Length: 20
|
||||
|
||||
name=John+Doe&age=25
|
||||
```
|
||||
|
||||
ご覧のように、ボディはクエリ文字列のようにエンコードされています。 これは、HTML のフォームで使用されるデフォルトの形式です。 Ajax では、JSON を使用してもっと複雑な構造のデータを処理することもできます。 また、multipart/form-data という別のエンコーディングタイプもあります。 これは、バイナリファイルをアップロードする場合に使用します。 この演習では、URL エンコードされたボディを使用します。 POST リクエストからのデータを解析するには、`body-parser` パッケージをインストールする必要があります。 このパッケージでは、さまざまな形式でデータをデコードできる一連のミドルウェアを使用できます。
|
||||
|
||||
# --instructions--
|
||||
|
||||
`package.json` に `body-parser` モジュールをインストールしてください。 次に、ファイルの先頭でモジュールを `require` 宣言してください。 そして、`bodyParser` という名前の変数に保存してください。 URL エンコードされたデータを処理するミドルウェアは、`bodyParser.urlencoded({extended: false})` によって返されます。 前のメソッド呼び出しで返された関数を `app.use()` に渡してください。 いつものように、ミドルウェアはそれに依存するすべてのルートよりも前にマウントする必要があります。
|
||||
|
||||
**注:** `extended` は、解析を使用する必要があるかどうかを `body-parser` に伝える設定オプションです。 `extended=false` の場合は、従来のエンコーディング `querystring` ライブラリを使用します。 `extended=true` の場合は、解析に `qs` ライブラリを使用します。
|
||||
|
||||
`extended=false` を使用する場合、値は文字列または配列のみとなります。 `querystring` を使用する場合に返されるオブジェクトは、デフォルトの JavaScript `Object` のプロトタイプを継承しません。つまり、`hasOwnProperty` や `toString` などの関数は利用できなくなります。 extended が有効な場合は、データの柔軟性が高まりますが、その点では JSON のほうが優れています。
|
||||
|
||||
# --hints--
|
||||
|
||||
「body-parser」ミドルウェアをマウントする必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/add-body-parser').then(
|
||||
(data) => {
|
||||
assert.isAbove(
|
||||
data.mountedAt,
|
||||
0,
|
||||
'"body-parser" is not mounted correctly'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,54 @@
|
||||
---
|
||||
id: 587d7fb1367417b2b2512bf2
|
||||
title: .env ファイルを使用する
|
||||
challengeType: 2
|
||||
forumTopicId: 301521
|
||||
dashedName: use-the--env-file
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
`.env` ファイルは、アプリケーションに環境変数を渡すために使用される隠しファイルです。 これは非公開ファイルであり、あなた以外誰もアクセスできません。 プライベートまたは非公開にしたいデータを保存する場合に使用することができます。 たとえば、外部サービスの API キーやデータベースの URI を保存できます。 また、設定オプションを保存する場合にも使用できます。 設定オプションを設定することで、コードを書き換えずにアプリの動作を変更できます。
|
||||
|
||||
環境変数にはアプリから `process.env.VAR_NAME` としてアクセスできます。 `process.env` オブジェクトは、グローバルの Node オブジェクトであり、変数は文字列として渡されます。 慣例により、変数名はすべて大文字で、単語はアンダースコアで区切られます。 `.env` はシェルファイルなので、名前や値を引用符で囲む必要はありません。 また、変数に値を割り当てる場合、等号記号の両側に空白を付けることはできず、`VAR_NAME=value` のようにする必要があります。 通常は、それぞれの変数定義を別々の行に記述します。
|
||||
|
||||
# --instructions--
|
||||
|
||||
環境変数を設定オプションとして追加しましょう。
|
||||
|
||||
プロジェクトディレクトリのルートに `.env` ファイルを作成し、変数 `MESSAGE_STYLE=uppercase` を保存してください。
|
||||
|
||||
次に、前回のチャレンジで作成した `/json` GET ルートハンドラーで、`process.env.MESSAGE_STYLE` が `uppercase` に等しい場合に、レスポンスオブジェクトのメッセージを大文字に変換してください。 レスポンスオブジェクトは、 `MESSAGE_STYLE` の値に応じて、`{"message": "Hello json"}` または `{"message": "HELLO JSON"}` のいずれかにする必要があります。
|
||||
|
||||
** 注: ** Replit を使用している場合は、 `.env` ファイルを作成することはできません。 代わりに、組み込みの <dfn>SECRETS</dfn> タブを使用して変数を追加してください。
|
||||
|
||||
ローカル環境で作業している場合は、`dotenv` パッケージが必要です。 このパッケージにより、環境変数が `.env` ファイルから `process.env` に読み込まれます。 `npm install dotenv` でインストールしてください。 次に、 `myApp.js` ファイルの先頭で、`require('dotenv').config()` を使用して変数をインポートして読み込んでください。
|
||||
|
||||
# --hints--
|
||||
|
||||
エンドポイント `/json` のレスポンスを、環境変数 `MESSAGE_STYLE` に従って変更する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/use-env-vars').then(
|
||||
(data) => {
|
||||
assert.isTrue(
|
||||
data.passed,
|
||||
'The response of "/json" does not change according to MESSAGE_STYLE'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,52 @@
|
||||
---
|
||||
id: 587d7fb3367417b2b2512bfc
|
||||
title: package.json に説明を追加する
|
||||
challengeType: 2
|
||||
forumTopicId: 301522
|
||||
dashedName: add-a-description-to-your-package-json
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
正しい package.json ファイルを構成する次の部分は `description` フィールドです。ここにはプロジェクトに関する簡潔で有益な説明があります。
|
||||
|
||||
将来 npm にパッケージを公開する予定がある場合は、この文字列で自分のアイデアをユーザーに伝え、パッケージをインストールするかどうかを決める際の参考にしてもらうことができます。 しかし、説明の用法はそれだけではなく、プロジェクトが何をするものなのかをまとめておくのに最適な場所です。 どの Node.js プロジェクトでも、他の開発者や将来の保守作業者、あるいは今後皆さん自身が、プロジェクトについて素早く理解できるようにしておくことが重要です。
|
||||
|
||||
プロジェクトの内容に関わらず、説明を記述しておくことをぜひ推奨します。 例を次に示します。
|
||||
|
||||
```json
|
||||
"description": "A project that does something awesome",
|
||||
```
|
||||
|
||||
# --instructions--
|
||||
|
||||
プロジェクトの package.json ファイルに `description` を追加してください。
|
||||
|
||||
**注: ** フィールド名には二重引用符 (") を使用し、複数フィールドの区切りにはコンマ (,) を使用してください。
|
||||
|
||||
# --hints--
|
||||
|
||||
package.json で、有効な「description」キーを記述する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/package.json').then(
|
||||
(data) => {
|
||||
var packJson = JSON.parse(data);
|
||||
assert(packJson.description, '"description" is missing');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,48 @@
|
||||
---
|
||||
id: 587d7fb4367417b2b2512bfe
|
||||
title: package.json にライセンスを追加する
|
||||
challengeType: 2
|
||||
forumTopicId: 301523
|
||||
dashedName: add-a-license-to-your-package-json
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
`license` フィールドでは、プロジェクトに対して許可することをユーザーに知らせます。
|
||||
|
||||
オープンソース プロジェクトのライセンスは MIT や BSD などが一般的です。 ライセンス情報は必須ではありません。ほとんどの国の著作権法では、特に断りがない限り、著作者に所有権が与えられます。 しかし、ユーザーができることとできないことを常に明確に記述しておくことを推奨します。 ライセンスフィールドの例を次に示します。
|
||||
|
||||
```json
|
||||
"license": "MIT",
|
||||
```
|
||||
|
||||
# --instructions--
|
||||
|
||||
プロジェクトの package.json ファイル内の `license` フィールドを適切に記述してください。
|
||||
|
||||
# --hints--
|
||||
|
||||
package.json で、有効な「license」キーを記述する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/package.json').then(
|
||||
(data) => {
|
||||
var packJson = JSON.parse(data);
|
||||
assert(packJson.license, '"license" is missing');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,46 @@
|
||||
---
|
||||
id: 587d7fb4367417b2b2512bff
|
||||
title: package.json にバージョンを追加する
|
||||
challengeType: 2
|
||||
forumTopicId: 301525
|
||||
dashedName: add-a-version-to-your-package-json
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
`version` は、package.json ファイルの必須フィールドの 1 つです。 このフィールドには、プロジェクトの現在のバージョンを記述します。 例を次に示します。
|
||||
|
||||
```json
|
||||
"version": "1.2.0",
|
||||
```
|
||||
|
||||
# --instructions--
|
||||
|
||||
プロジェクトの package.json ファイルに `version` を追加してください。
|
||||
|
||||
# --hints--
|
||||
|
||||
package.json で、有効な「version」キーを記述する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/package.json').then(
|
||||
(data) => {
|
||||
var packJson = JSON.parse(data);
|
||||
assert(packJson.version, '"version" is missing');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,84 @@
|
||||
---
|
||||
id: 587d7fb4367417b2b2512bfd
|
||||
title: package.json にキーワードを追加する
|
||||
challengeType: 2
|
||||
forumTopicId: 301526
|
||||
dashedName: add-keywords-to-your-package-json
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
`keywords` フィールドでは、関連するキーワードを使用してプロジェクトを記述できます。 例を次に示します。
|
||||
|
||||
```json
|
||||
"keywords": [ "descriptive", "related", "words" ],
|
||||
```
|
||||
|
||||
ご覧のとおり、このフィールドは二重引用符で囲まれた文字列の配列として構成されています。
|
||||
|
||||
# --instructions--
|
||||
|
||||
プロジェクトの package.json ファイルの `keywords` フィールドに、適切な文字列の配列を追加してください。
|
||||
|
||||
キーワードの 1 つを「freecodecamp」にする必要があります。
|
||||
|
||||
# --hints--
|
||||
|
||||
package.json で、有効な 「keywords」キーを記述する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/package.json').then(
|
||||
(data) => {
|
||||
var packJson = JSON.parse(data);
|
||||
assert(packJson.keywords, '"keywords" is missing');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
「keywords」フィールドは、配列である必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/package.json').then(
|
||||
(data) => {
|
||||
var packJson = JSON.parse(data);
|
||||
assert.isArray(packJson.keywords, '"keywords" is not an array');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
「keywords」に「freecodecamp」を含める必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/package.json').then(
|
||||
(data) => {
|
||||
var packJson = JSON.parse(data);
|
||||
assert.include(
|
||||
packJson.keywords,
|
||||
'freecodecamp',
|
||||
'"keywords" does not include "freecodecamp"'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,77 @@
|
||||
---
|
||||
id: 587d7fb4367417b2b2512c00
|
||||
title: npm から外部パッケージを使用してプロジェクトを拡張する
|
||||
challengeType: 2
|
||||
forumTopicId: 301527
|
||||
dashedName: expand-your-project-with-external-packages-from-npm
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
パッケージマネージャーを使用する最大の理由の 1 つは、強力な依存関係管理です。 新しいコンピューターでプロジェクトを準備する場合、そのたびにすべての依存関係を手動で確認する必要はなく、npm が自動的にすべてをインストールしてくれます。 しかし、npm はプロジェクトに必要なものをどのようにして正確に知ることができるのでしょうか? package.json ファイルの `dependencies` セクションを見てください。
|
||||
|
||||
このセクションには、プロジェクトが必要とするパッケージが次のような形式で保存されています。
|
||||
|
||||
```json
|
||||
"dependencies": {
|
||||
"package-name": "version",
|
||||
"express": "4.14.0"
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
# --instructions--
|
||||
|
||||
Package.json ファイルの `dependencies` フィールドに、"moment" パッケージのバージョン "2.14.0" を追加してください。
|
||||
|
||||
** 注: ** moment は、時間と日付を扱う際に便利なライブラリです。
|
||||
|
||||
# --hints--
|
||||
|
||||
「dependencies」には、「moment」を含める必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/package.json').then(
|
||||
(data) => {
|
||||
var packJson = JSON.parse(data);
|
||||
assert.property(
|
||||
packJson.dependencies,
|
||||
'moment',
|
||||
'"dependencies" does not include "moment"'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
「moment」のバージョンを「2.14.0」にする必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/package.json').then(
|
||||
(data) => {
|
||||
var packJson = JSON.parse(data);
|
||||
assert.match(
|
||||
packJson.dependencies.moment,
|
||||
/^[\^\~]?2\.14\.0/,
|
||||
'Wrong version of "moment" installed. It should be 2.14.0'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,60 @@
|
||||
---
|
||||
id: 587d7fb3367417b2b2512bfb
|
||||
title: 'Node.js プロジェクトまたは npm パッケージの中心となる package.json を使用する'
|
||||
challengeType: 2
|
||||
forumTopicId: 301528
|
||||
dashedName: how-to-use-package-json-the-core-of-any-node-js-project-or-npm-package
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
チャレンジに取り組むにあたり、以下の方法のうち 1 つを用いてコードを記述します。
|
||||
|
||||
- [GitHub レポジトリ](https://github.com/freeCodeCamp/boilerplate-npm/)をクローンし、ローカル環境でチャレンジを完了させる。
|
||||
- [Replit 始動プロジェクト](https://replit.com/github/freeCodeCamp/boilerplate-npm)を使用してチャレンジを完了させる。
|
||||
- 使い慣れたサイトビルダーを使用してプロジェクトを完了させる。 必ず GitHub リポジトリのすべてのファイルを取り込む。
|
||||
|
||||
完了したら、プロジェクトの動作デモをどこか公開の場にホストしてください。 そして、`Solution Link` フィールドでデモへの URL を送信してください。 必要に応じて、`GitHub Link` フィールドでプロジェクトのソースコードへのリンクを送信してください。
|
||||
|
||||
`package.json` ファイルは、Node.js プロジェクトまたは npm パッケージの中心となります。 HTML ドキュメントの <head> セクションでウェブページの内容を記述するのと同じように、このファイルにはプロジェクトに関する情報を保存します。 ファイルは単一の JSON オブジェクトで構成され、そこに情報がキーと値のペアで保存されます。 必須のフィールドは「name」と「version」の 2 つのみですが、将来のユーザーや保守作業者に役立つように、プロジェクトに関する追加情報を記述しておくことをお勧めします。
|
||||
|
||||
プロジェクトのファイルツリーを見ると、package.json ファイルはツリーの最上位のレベルにあります。 以降のチャレンジではこのファイルに改良を加えていきます。
|
||||
|
||||
このファイルで最も一般的な情報の 1 つは、 `author` フィールドです。 これはプロジェクトの作成者を指定するもので、連絡先などの詳細を記した文字列またはオブジェクトで構成できます。 より大きなプロジェクトではオブジェクトが推奨されますが、このプロジェクトでは次の例のような単純な文字列を使用します。
|
||||
|
||||
```json
|
||||
"author": "Jane Doe",
|
||||
```
|
||||
|
||||
# --instructions--
|
||||
|
||||
package.json ファイルで、プロジェクトの `author` の名前を追加してください。
|
||||
|
||||
**注: ** JSON を記述しているので、すべてのフィールド名で二重引用符 (") を使用し、コンマ (,) で区切る必要があります。
|
||||
|
||||
# --hints--
|
||||
|
||||
package.json で、有効な「author」キーを記述する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/package.json').then(
|
||||
(data) => {
|
||||
var packJson = JSON.parse(data);
|
||||
assert(packJson.author, '"author" is missing');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,73 @@
|
||||
---
|
||||
id: 587d7fb5367417b2b2512c01
|
||||
title: セマンティック バージョニングを理解して npm の依存関係を管理する
|
||||
challengeType: 2
|
||||
forumTopicId: 301529
|
||||
dashedName: manage-npm-dependencies-by-understanding-semantic-versioning
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
package.json ファイルの依存関係セクションにある npm パッケージの `Versions` は、いわゆるセンマティック バージョニング (SemVer) と呼ばれる決まりに従っています。SemVer とは、依存関係の管理を容易にすることを目的としたソフトウェアバージョン管理の業界標準です。 npm に公開されるライブラリ、フレームワークおよびその他のツールでは、プロジェクトを更新する場合にどのような変更がプロジェクトに加えられるのかを明確に伝えるために、SemVer を使用する必要があります。
|
||||
|
||||
SemVer を理解しておくと、外部の依存関係を使用するソフトウェアを開発する場合 (ほとんどの場合はそうです) に役立ちます。 将来、これらの数字を理解しておけば、昨日まで動いていたものが今日になって突然動かなくなった理由を理解できなくても、誤ってプロジェクトに破壊的な変更を加えることはなくなります。 公式ウェブサイトによれば、センマティック バージョニングの仕組みは以下のようになっています。
|
||||
|
||||
```json
|
||||
"package": "MAJOR.MINOR.PATCH"
|
||||
```
|
||||
|
||||
互換性のない API 変更を加える場合は、MAJOR バージョンをインクリメントする必要があります。 後方互換性のある方法で機能を追加する場合は、MINOR バージョンをインクリメントする必要があります。 後方互換性のあるバグ修正を行う場合は、PATCH バージョンをインクリメントする必要があります。 つまり、PATCH はバグ修正であり、MINOR は新機能の追加ですが、どちらも以前の機能を壊すことはありません。 最後に、MAJOR は、以前のバージョンでは動作しないような変更を追加します。
|
||||
|
||||
# --instructions--
|
||||
|
||||
package.json ファイルの依存関係セクションで、 moment の `version` を変更して、MAJOR バージョンを 2、MINOR バージョンを 10、PATCH バージョンを 2 にしてください。
|
||||
|
||||
# --hints--
|
||||
|
||||
「dependencies」に「moment」を含める必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/package.json').then(
|
||||
(data) => {
|
||||
var packJson = JSON.parse(data);
|
||||
assert.property(
|
||||
packJson.dependencies,
|
||||
'moment',
|
||||
'"dependencies" does not include "moment"'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
「moment」のバージョンを「2.10.2」にする必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/package.json').then(
|
||||
(data) => {
|
||||
var packJson = JSON.parse(data);
|
||||
assert.equal(
|
||||
packJson.dependencies.moment,
|
||||
'2.10.2',
|
||||
'Wrong version of "moment". It should be 2.10.2'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,52 @@
|
||||
---
|
||||
id: 587d7fb5367417b2b2512c04
|
||||
title: 依存関係からパッケージを削除する
|
||||
challengeType: 2
|
||||
forumTopicId: 301530
|
||||
dashedName: remove-a-package-from-your-dependencies
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
package.json の依存関係セクションを使用して、いくつかの方法でプロジェクトの依存関係を管理できることがわかりました。 また、外部パッケージをファイルに追加する作業、チルダやキャレットなどの特殊文字を使用してどのバージョンが必要なのかを npm に指示する作業も行いました。
|
||||
|
||||
しかし、不要になった外部パッケージを削除したい場合はどうすればよいでしょうか? すでに推察している人もいるかもしれませんが、そのパッケージに対応するキーと値のペアを依存関係から削除するだけです。
|
||||
|
||||
この方法は、package.json 内の他のフィールドの削除にも適用されます。
|
||||
|
||||
# --instructions--
|
||||
|
||||
moment パッケージを依存関係から削除してください。
|
||||
|
||||
**注:** 削除した後にコンマの数が正しいことを確認してください。
|
||||
|
||||
# --hints--
|
||||
|
||||
「dependencies」に「moment」を含めないようにする必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/package.json').then(
|
||||
(data) => {
|
||||
var packJson = JSON.parse(data);
|
||||
assert.notProperty(
|
||||
packJson.dependencies,
|
||||
'moment',
|
||||
'"dependencies" still includes "moment"'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,75 @@
|
||||
---
|
||||
id: 587d7fb5367417b2b2512c03
|
||||
title: キャレット文字を使用して依存関係の最新の MINOR バージョンを使用する
|
||||
challengeType: 2
|
||||
forumTopicId: 301531
|
||||
dashedName: use-the-caret-character-to-use-the-latest-minor-version-of-a-dependency
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
前回のチャレンジで学んだチルダと同じように、npm では依存関係の最新の PATCH をインストールできます。npm でキャレット (`^`) を使用すると、将来のアップデートもインストールできます。 両者の違いは、キャレットでは MINOR アップデートと PATCH の両方が可能であることです。
|
||||
|
||||
moment の現在のバージョンを「~2.10.2」にすると、npm で最新の 2.10.x バージョンをインストールできます。 代わりにバージョン プレフィックスとしてキャレット (^) を使用すると、npm では任意の 2.x.x バージョンへ更新できます。
|
||||
|
||||
```json
|
||||
"package": "^1.3.8"
|
||||
```
|
||||
|
||||
この記述では、パッケージを任意の 1.x.x バージョンへ更新できます。
|
||||
|
||||
# --instructions--
|
||||
|
||||
依存関係内の moment のバージョンのプレフィックスとしてキャレット (`^`) を使用し、npm によって任意の新しい MINOR リリースへ更新できるようにしてください。
|
||||
|
||||
**注:** バージョン番号自体は変更しないでください。
|
||||
|
||||
# --hints--
|
||||
|
||||
「dependencies」に「moment」を含める必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/package.json').then(
|
||||
(data) => {
|
||||
var packJson = JSON.parse(data);
|
||||
assert.property(
|
||||
packJson.dependencies,
|
||||
'moment',
|
||||
'"dependencies" does not include "moment"'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
「moment」バージョンは、「^2.x.x」と一致する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/package.json').then(
|
||||
(data) => {
|
||||
var packJson = JSON.parse(data);
|
||||
assert.match(
|
||||
packJson.dependencies.moment,
|
||||
/^\^2\./,
|
||||
'Wrong version of "moment". It should be ^2.10.2'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,75 @@
|
||||
---
|
||||
id: 587d7fb5367417b2b2512c02
|
||||
title: チルダ文字を使用して常に依存関係の最新の PATCH バージョンを使用する
|
||||
challengeType: 2
|
||||
forumTopicId: 301532
|
||||
dashedName: use-the-tilde-character-to-always-use-the-latest-patch-version-of-a-dependency
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
前回のチャレンジでは、npm に特定のバージョンのパッケージのみを含めるよう指示しました。 この方法は、プロジェクトのさまざまな部分について互いの互換性を保つ必要がある場合に、依存関係を固定するのに便利です。 しかし、ほとんどのケースではバグ修正を省くのは好ましくありません。これは、 バグ修正には重要なセキュリティパッチが含まれていることが多く、適用しても現在の動作を壊すことはない (だろうと考える) からです。
|
||||
|
||||
依存関係のバージョンの前にプレフィックスとしてチルダ (`~`) 文字を付けると、npm の依存関係を最新の PATCH バージョンに更新することができます。 1.3.x バージョンへのアップデートを許可する方法の例を次に示します。
|
||||
|
||||
```json
|
||||
"package": "~1.3.8"
|
||||
```
|
||||
|
||||
# --instructions--
|
||||
|
||||
package.json ファイルに、npm に許可する moment のアップグレード方法が示されていますが、現在のルールでは特定のバージョン (2.10.2) が使用されます。 しかし、ここでは最新の 2.10.x バージョンを許可したいとします。
|
||||
|
||||
依存関係内の moment のバージョンの前にプレフィックスとしてチルダ (`~`) 文字を使用し、npm によって新しい PATCH リリースに更新されるようにしてください。
|
||||
|
||||
**注:** バージョン番号自体は変更しないでください。
|
||||
|
||||
# --hints--
|
||||
|
||||
「dependencies」に「moment」を含める必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/package.json').then(
|
||||
(data) => {
|
||||
var packJson = JSON.parse(data);
|
||||
assert.property(
|
||||
packJson.dependencies,
|
||||
'moment',
|
||||
'"dependencies" does not include "moment"'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
「moment」のバージョンを「~2.10.2」にする必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/package.json').then(
|
||||
(data) => {
|
||||
var packJson = JSON.parse(data);
|
||||
assert.match(
|
||||
packJson.dependencies.moment,
|
||||
/^\~2\.10\.2/,
|
||||
'Wrong version of "moment". It should be ~2.10.2'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,76 @@
|
||||
---
|
||||
id: 587d7fb9367417b2b2512c12
|
||||
title: 検索クエリヘルパーをチェーンして検索結果を絞り込む
|
||||
challengeType: 2
|
||||
forumTopicId: 301533
|
||||
dashedName: chain-search-query-helpers-to-narrow-search-results
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
`Model.find()` (または他の検索メソッド) に最後の引数としてコールバックを渡さない場合、クエリは実行されません。 クエリを変数に保存しておき、後で使用することができます。 この種類のオブジェクトでは、チェーン シンタックスを使用してクエリを構築できます。 実際の db 検索は、最後にメソッド `.exec()` をチェーンしたときに実行されます。 この最後のメソッドに、常にコールバックを渡す必要があります。 多くのクエリヘルパーがありますが、ここでは最もよく使用されるものを使用します。
|
||||
|
||||
# --instructions--
|
||||
|
||||
`queryChain` 関数を変更して、`foodToSearch` という変数で指定された食べ物が好きな人を見つけてください。 `name` 順に並べ替え、結果を 2 つのドキュメントに限定して、年齢を非表示にしてください。 `.find()`、`.sort()`、`.limit()`、`.select()` および `.exec()` をチェーンしてください。 `done(err, data)` コールバックを `exec()` に渡してください。
|
||||
|
||||
# --hints--
|
||||
|
||||
クエリヘルパーを正しくチェーンする必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.ajax({
|
||||
url: getUserInput('url') + '/_api/query-tools',
|
||||
type: 'POST',
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify([
|
||||
{ name: 'Pablo', age: 26, favoriteFoods: ['burrito', 'hot-dog'] },
|
||||
{ name: 'Bob', age: 23, favoriteFoods: ['pizza', 'nachos'] },
|
||||
{ name: 'Ashley', age: 32, favoriteFoods: ['steak', 'burrito'] },
|
||||
{ name: 'Mario', age: 51, favoriteFoods: ['burrito', 'prosciutto'] }
|
||||
])
|
||||
}).then(
|
||||
(data) => {
|
||||
assert.isArray(data, 'the response should be an Array');
|
||||
assert.equal(
|
||||
data.length,
|
||||
2,
|
||||
'the data array length is not what expected'
|
||||
);
|
||||
assert.notProperty(
|
||||
data[0],
|
||||
'age',
|
||||
'The returned first item has too many properties'
|
||||
);
|
||||
assert.equal(
|
||||
data[0].name,
|
||||
'Ashley',
|
||||
'The returned first item name is not what expected'
|
||||
);
|
||||
assert.notProperty(
|
||||
data[1],
|
||||
'age',
|
||||
'The returned second item has too many properties'
|
||||
);
|
||||
assert.equal(
|
||||
data[1].name,
|
||||
'Mario',
|
||||
'The returned second item name is not what expected'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,88 @@
|
||||
---
|
||||
id: 587d7fb6367417b2b2512c07
|
||||
title: モデルを作成する
|
||||
challengeType: 2
|
||||
forumTopicId: 301535
|
||||
dashedName: create-a-model
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
**C**RUD パート I - 作成する
|
||||
|
||||
まず最初に、スキーマが必要です。 各スキーマは、MongoDB コレクションにマップされます。 スキーマにより、コレクション内のドキュメントの形状を定義します。 スキーマは、モデルを組み立てるための基礎的な要素になります。 スキーマをネストさせて複雑なモデルを作成することもできますが、この例ではシンプルな構造にします。 モデルにより、ドキュメントと呼ばれるオブジェクトのインスタンスを作成できます。
|
||||
|
||||
Replit は実際のサーバーであり、実際のサーバーではデータベースとのやり取りはハンドラ関数の中で行います。 これらの関数は、何らかのイベントが発生した時に実行されます (たとえば、誰かが API 上のエンドポイントにアクセスしたとき)。 演習でも同じアプローチに従います。 `done()` 関数は、挿入、検索、更新または削除などの非同期操作を完了した後に処理を続行できることを示すコールバック関数です。 Node の規約に従い、成功時には `done(null, data)` を呼び出し、エラー時には `done(err)` を呼び出します。
|
||||
|
||||
警告 - リモートサービスとのやり取りではエラーが発生する可能性があります!
|
||||
|
||||
```js
|
||||
/* Example */
|
||||
|
||||
const someFunc = function(done) {
|
||||
//... do something (risky) ...
|
||||
if (error) return done(error);
|
||||
done(null, result);
|
||||
};
|
||||
```
|
||||
|
||||
# --instructions--
|
||||
|
||||
次のプロトタイプを持つ `personSchema` というパーソンスキーマを作成してください。
|
||||
|
||||
```markup
|
||||
- Person Prototype -
|
||||
--------------------
|
||||
name : string [required]
|
||||
age : number
|
||||
favoriteFoods : array of strings (*)
|
||||
```
|
||||
|
||||
Mongoose の基本的なスキーマタイプを使用してください。 フィールドを追加したい場合は、required や unique といった単純なバリデーターを使用し、デフォルト値を設定してください。 [Mongoose のドキュメント](http://mongoosejs.com/docs/guide.html) を参照してください。
|
||||
|
||||
次に、`personSchema` から `Person` というモデルを作成してください。
|
||||
|
||||
# --hints--
|
||||
|
||||
Mongoose スキーマからインスタンスを正しく作成する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.post(getUserInput('url') + '/_api/mongoose-model', {
|
||||
name: 'Mike',
|
||||
age: 28,
|
||||
favoriteFoods: ['pizza', 'cheese']
|
||||
}).then(
|
||||
(data) => {
|
||||
assert.equal(data.name, 'Mike', '"model.name" is not what expected');
|
||||
assert.equal(data.age, '28', '"model.age" is not what expected');
|
||||
assert.isArray(
|
||||
data.favoriteFoods,
|
||||
'"model.favoriteFoods" is not an Array'
|
||||
);
|
||||
assert.include(
|
||||
data.favoriteFoods,
|
||||
'pizza',
|
||||
'"model.favoriteFoods" does not include the expected items'
|
||||
);
|
||||
assert.include(
|
||||
data.favoriteFoods,
|
||||
'cheese',
|
||||
'"model.favoriteFoods" does not include the expected items'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,56 @@
|
||||
---
|
||||
id: 587d7fb6367417b2b2512c09
|
||||
title: モデルのレコードを作成および保存する
|
||||
challengeType: 2
|
||||
forumTopicId: 301536
|
||||
dashedName: create-and-save-a-record-of-a-model
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
このチャレンジでは、モデルのレコードを作成して保存する必要があります。
|
||||
|
||||
# --instructions--
|
||||
|
||||
`createAndSavePerson` 関数内で、以前に構築した `Person` モデル コンストラクターを使用してドキュメント インスタンスを作成します。 フィールド `name`、`age` および `favoriteFoods` を持つオブジェクトをコンストラクターに渡します。 これらのタイプは、`personSchema` 内のものに準拠していなければなりません。 次に、返されるドキュメント インスタンスに対してメソッド `document.save()` を呼び出します。 Node の規約を使用してコールバックを渡します。 これはよく使用するパターンです。以下の CRUD メソッドはすべて、このようなコールバック関数を最後の引数として受け取ります。
|
||||
|
||||
```js
|
||||
/* Example */
|
||||
|
||||
// ...
|
||||
person.save(function(err, data) {
|
||||
// ...do your stuff here...
|
||||
});
|
||||
```
|
||||
|
||||
# --hints--
|
||||
|
||||
db アイテムを正しく作成して保存する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/create-and-save-person').then(
|
||||
(data) => {
|
||||
assert.isString(data.name, '"item.name" should be a String');
|
||||
assert.isNumber(data.age, '28', '"item.age" should be a Number');
|
||||
assert.isArray(
|
||||
data.favoriteFoods,
|
||||
'"item.favoriteFoods" should be an Array'
|
||||
);
|
||||
assert.equal(data.__v, 0, 'The db item should be not previously edited');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,68 @@
|
||||
---
|
||||
id: 587d7fb7367417b2b2512c0a
|
||||
title: Model.create() で多数のレコードを作成する
|
||||
challengeType: 2
|
||||
forumTopicId: 301537
|
||||
dashedName: create-many-records-with-model-create
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
最初のデータでデータベースをシードする場合などでは、モデルのインスタンスを多数作成する必要があります。 `Model.create()` は、最初の引数として `[{name: 'John', ...}, {...}, ...]` のようなオブジェクト配列を受け取り、それらのすべてを db に保存します。
|
||||
|
||||
# --instructions--
|
||||
|
||||
`createManyPeople` 関数を変更して、`Model.create()` で引数 `arrayOfPeople` を使用して多数の人を作成してください。
|
||||
|
||||
**注:** 前回の演習でインスタンス化したモデルを再利用できます。
|
||||
|
||||
# --hints--
|
||||
|
||||
一度に多数の db アイテムを正しく作成する必要がります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.ajax({
|
||||
url: getUserInput('url') + '/_api/create-many-people',
|
||||
type: 'POST',
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify([
|
||||
{ name: 'John', age: 24, favoriteFoods: ['pizza', 'salad'] },
|
||||
{ name: 'Mary', age: 21, favoriteFoods: ['onions', 'chicken'] }
|
||||
])
|
||||
}).then(
|
||||
(data) => {
|
||||
assert.isArray(data, 'the response should be an array');
|
||||
assert.equal(
|
||||
data.length,
|
||||
2,
|
||||
'the response does not contain the expected number of items'
|
||||
);
|
||||
assert.equal(data[0].name, 'John', 'The first item is not correct');
|
||||
assert.equal(
|
||||
data[0].__v,
|
||||
0,
|
||||
'The first item should be not previously edited'
|
||||
);
|
||||
assert.equal(data[1].name, 'Mary', 'The second item is not correct');
|
||||
assert.equal(
|
||||
data[1].__v,
|
||||
0,
|
||||
'The second item should be not previously edited'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,57 @@
|
||||
---
|
||||
id: 587d7fb8367417b2b2512c11
|
||||
title: Model.remove() で多数のドキュメントを削除する
|
||||
challengeType: 2
|
||||
forumTopicId: 301538
|
||||
dashedName: delete-many-documents-with-model-remove
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
`Model.remove()` は、指定された条件に一致するすべてのドキュメントを削除するのに便利です。
|
||||
|
||||
# --instructions--
|
||||
|
||||
`removeManyPeople` を変更し、`Model.remove()` を使用して、名前が変数 `nameToRemove` 内にある人をすべて削除してください。 それを、`name` フィールドを設定したクエリドキュメントと、コールバックに渡してください。
|
||||
|
||||
**注:** `Model.remove()` は、削除したドキュメントを返さず、操作の結果を含む JSON オブジェクトと、影響を受けたアイテムの数を返します。 テストで使用するので、忘れずに `done()` コールバックに渡してください。
|
||||
|
||||
# --hints--
|
||||
|
||||
一度に多数のアイテムを正しく削除する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.ajax({
|
||||
url: getUserInput('url') + '/_api/remove-many-people',
|
||||
type: 'POST',
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify([
|
||||
{ name: 'Mary', age: 16, favoriteFoods: ['lollipop'] },
|
||||
{ name: 'Mary', age: 21, favoriteFoods: ['steak'] }
|
||||
])
|
||||
}).then(
|
||||
(data) => {
|
||||
assert.isTrue(!!data.ok, 'The mongo stats are not what expected');
|
||||
assert.equal(
|
||||
data.n,
|
||||
2,
|
||||
'The number of items affected is not what expected'
|
||||
);
|
||||
assert.equal(data.count, 0, 'the db items count is not what expected');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,53 @@
|
||||
---
|
||||
id: 587d7fb8367417b2b2512c10
|
||||
title: Model.findByIdAndRemove を使用して 1 つのドキュメントを削除する
|
||||
challengeType: 2
|
||||
forumTopicId: 301539
|
||||
dashedName: delete-one-document-using-model-findbyidandremove
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
`findByIdAndRemove` と `findOneAndRemove` も、前回の更新メソッドと同様です。 これらは、削除したドキュメントを db に渡します。 いつものように、関数引数 `personId` を検索キーとして使用してください。
|
||||
|
||||
# --instructions--
|
||||
|
||||
`removeById` 関数を変更して、1 人の個人を `_id` で削除してください。 `findByIdAndRemove()` または `findOneAndRemove()` メソッドのいずれかを使用する必要があります。
|
||||
|
||||
# --hints--
|
||||
|
||||
アイテムを正しく削除する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.post(getUserInput('url') + '/_api/remove-one-person', {
|
||||
name: 'Jason Bourne',
|
||||
age: 36,
|
||||
favoriteFoods: ['apples']
|
||||
}).then(
|
||||
(data) => {
|
||||
assert.equal(data.name, 'Jason Bourne', 'item.name is not what expected');
|
||||
assert.equal(data.age, 36, 'item.age is not what expected');
|
||||
assert.deepEqual(
|
||||
data.favoriteFoods,
|
||||
['apples'],
|
||||
'item.favoriteFoods is not what expected'
|
||||
);
|
||||
assert.equal(data.__v, 0);
|
||||
assert.equal(data.count, 0, 'the db items count is not what expected');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,85 @@
|
||||
---
|
||||
id: 587d7fb6367417b2b2512c06
|
||||
title: Mongoose をインストールして設定する
|
||||
challengeType: 2
|
||||
forumTopicId: 301540
|
||||
dashedName: install-and-set-up-mongoose
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
チャレンジに取り組むにあたり、以下の方法のうち 1 つを用いてコードを記述します。
|
||||
|
||||
- [GitHub レポジトリ](https://github.com/freeCodeCamp/boilerplate-mongomongoose/)をクローンし、ローカル環境でチャレンジを完了させる。
|
||||
- [Replit 始動プロジェクト](https://replit.com/github/freeCodeCamp/boilerplate-mongomongoose)を使用してチャレンジを完了させる。
|
||||
- 使い慣れたサイトビルダーを使用してプロジェクトを完了させる。 必ず GitHub リポジトリのすべてのファイルを取り込む。
|
||||
|
||||
完了したら、プロジェクトの動作デモをどこか公開の場にホストしてください。 そして、`Solution Link` フィールドでデモへの URL を送信してください。
|
||||
|
||||
このチャレンジでは、MongoDB の Atlas データベースを設定し、それに接続するために必要なパッケージをインポートします。
|
||||
|
||||
<a href='https://www.freecodecamp.org/news/get-started-with-mongodb-atlas/' rel='noopener noreferrer' target='_blank'>チュートリアル</a>に従い、ホストされたデータベースを MongoDB Atlas に設定してください。
|
||||
|
||||
# --instructions--
|
||||
|
||||
`mongodb@~3.6.0` と `mongoose@~5.4.0` をプロジェクトの `package.json` に追加してください。 次に、`myApp.js` で `mongoose` を require してください。 `.env` ファイルを作成し、`MONGO_URI` 変数を追加してください。 変数の値は、MongoDB Atlas データベースの URI である必要があります。 URI は一重引用符または二重引用符で囲んでください。また、環境変数では `=` の前後に空白を使用できないことに注意してください。 たとえば、`MONGO_URI='VALUE'` などとします。 完了したら、次のシンタックスを使用してデータベースに接続してください。
|
||||
|
||||
```js
|
||||
mongoose.connect(<Your URI>, { useNewUrlParser: true, useUnifiedTopology: true });
|
||||
```
|
||||
|
||||
# --hints--
|
||||
|
||||
「mongodb」依存関係が package.json にある必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/file/package.json').then(
|
||||
(data) => {
|
||||
var packJson = JSON.parse(data);
|
||||
assert.property(packJson.dependencies, 'mongodb');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
「mongoose」依存関係が package.json にある必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/file/package.json').then(
|
||||
(data) => {
|
||||
var packJson = JSON.parse(data);
|
||||
assert.property(packJson.dependencies, 'mongoose');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
「mongoose」をデータベースに接続する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/is-mongoose-ok').then(
|
||||
(data) => {
|
||||
assert.isTrue(data.isMongooseOk, 'mongoose is not connected');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,54 @@
|
||||
---
|
||||
id: 587d7fb8367417b2b2512c0e
|
||||
title: '検索、編集、保存により古典的な更新を実行する'
|
||||
challengeType: 2
|
||||
forumTopicId: 301541
|
||||
dashedName: perform-classic-updates-by-running-find-edit-then-save
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
ドキュメントを編集して何とか使えるようにしたい (たとえば、サーバーのレスポンスでドキュメントを返したい) 場合、従来はこの手順で更新を実行する必要がありました。 Mongoose には専用の更新メソッドとして、`Model.update()` があります。 これは、低レベルの mongo ドライバにバインドされています。 特定の条件に一致する多くの文書をまとめて編集することができますが、更新した文書は返さず、「status」メッセージのみ返します。 また、単に直接 mongoドライバを呼び出すだけなので、モデルの検証が困難になります。
|
||||
|
||||
# --instructions--
|
||||
|
||||
`findEditThenSave` 関数を変更し、パラメーター `personId` を検索キーとして `_id` で個人を検索してください (上記メソッドのいずれかを使用してください) 。 検索したその個人の `favoriteFoods` リストに、`"hamburger"` を追加してください (`Array.push()` を使用することが可能です)。 そして、検索コールバックの中で、更新した `Person` を `save()` してください。
|
||||
|
||||
**注:** スキーマでタイプ (たとえば、`[String]`) を指定せずに `favoriteFoods` を配列として宣言した場合は、多少わかりにくくなるかもしれません。 その場合、`favoriteFoods` はデフォルトで Mixed タイプに設定されるので、`document.markModified('edited-field')` を使用して手動で編集済みにマークする必要があります。 [Mongoose のドキュメント](https://mongoosejs.com/docs/schematypes.html#Mixed) を参照してください。
|
||||
|
||||
# --hints--
|
||||
|
||||
アイテムの検索、編集、更新を正しく行う必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.post(getUserInput('url') + '/_api/find-edit-save', {
|
||||
name: 'Poldo',
|
||||
age: 40,
|
||||
favoriteFoods: ['spaghetti']
|
||||
}).then(
|
||||
(data) => {
|
||||
assert.equal(data.name, 'Poldo', 'item.name is not what is expected');
|
||||
assert.equal(data.age, 40, 'item.age is not what expected');
|
||||
assert.deepEqual(
|
||||
data.favoriteFoods,
|
||||
['spaghetti', 'hamburger'],
|
||||
'item.favoriteFoods is not what expected'
|
||||
);
|
||||
assert.equal(data.__v, 1, 'The item should be previously edited');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,58 @@
|
||||
---
|
||||
id: 587d7fb8367417b2b2512c0f
|
||||
title: Model.findOneAndUpdate() を使用して、ドキュメントの新しい更新を実行する
|
||||
challengeType: 2
|
||||
forumTopicId: 301542
|
||||
dashedName: perform-new-updates-on-a-document-using-model-findoneandupdate
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
最新バージョンの Mongoose は、ドキュメントの更新を簡素化するメソッドを備えています。 しかし、いくつかの高度な機能 (プレ/ポストフック、検証) は、上記のアプローチに対しては異なる動作をします。そのため、従来のメソッドが今でも多くの状況で役に立ちます。 `findByIdAndUpdate()` は、id で検索するときに使用できます。
|
||||
|
||||
# --instructions--
|
||||
|
||||
`findAndUpdate` 関数を変更して、`Name` で個人を検索し、年齢を `20` に設定してください。 検索キーとして、関数パラメーター `personName` を使用してください。
|
||||
|
||||
**注:** 更新したドキュメントを返す必要があります。 そのためには、オプションのドキュメント `{ new: true }` を `findOneAndUpdate()` の第 3 引数として渡す必要があります。 デフォルトでは、これらのメソッドは変更前のオブジェクトを返します。
|
||||
|
||||
# --hints--
|
||||
|
||||
アイテムを正しく findOneAndUpdate する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.post(getUserInput('url') + '/_api/find-one-update', {
|
||||
name: 'Dorian Gray',
|
||||
age: 35,
|
||||
favoriteFoods: ['unknown']
|
||||
}).then(
|
||||
(data) => {
|
||||
assert.equal(data.name, 'Dorian Gray', 'item.name is not what expected');
|
||||
assert.equal(data.age, 20, 'item.age is not what expected');
|
||||
assert.deepEqual(
|
||||
data.favoriteFoods,
|
||||
['unknown'],
|
||||
'item.favoriteFoods is not what expected'
|
||||
);
|
||||
assert.equal(
|
||||
data.__v,
|
||||
0,
|
||||
'findOneAndUpdate does not increment version by design!'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,53 @@
|
||||
---
|
||||
id: 587d7fb7367417b2b2512c0b
|
||||
title: Model.find() を使用してデータベースを検索する
|
||||
challengeType: 2
|
||||
forumTopicId: 301543
|
||||
dashedName: use-model-find-to-search-your-database
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
最もシンプルな用法では、`Model.find()` は最初の引数としてクエリドキュメント (JSON オブジェクト) を受け取り、次にコールバックを受け取ります。 そして、一致する配列を返します。 このメソッドは非常に幅広い検索オプションに対応しています。 詳細はドキュメントをご覧ください。
|
||||
|
||||
# --instructions--
|
||||
|
||||
`findPeopleByName` 関数を変更し、<code>Model.find() -\> [Person]</code> を使用して、指定した名前を持つすべての人を検索してください。
|
||||
|
||||
検索キーとして関数引数 `personName` を使用してください。
|
||||
|
||||
# --hints--
|
||||
|
||||
条件に対応するすべてのアイテムを正しく見つける必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.post(getUserInput('url') + '/_api/find-all-by-name', {
|
||||
name: 'r@nd0mN4m3',
|
||||
age: 24,
|
||||
favoriteFoods: ['pizza']
|
||||
}).then(
|
||||
(data) => {
|
||||
assert.isArray(data, 'the response should be an Array');
|
||||
assert.equal(
|
||||
data[0].name,
|
||||
'r@nd0mN4m3',
|
||||
'item.name is not what expected'
|
||||
);
|
||||
assert.equal(data[0].__v, 0, 'The item should be not previously edited');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,48 @@
|
||||
---
|
||||
id: 587d7fb7367417b2b2512c0d
|
||||
title: Model.findById() を使用して _id でデータベースを検索する
|
||||
challengeType: 2
|
||||
forumTopicId: 301544
|
||||
dashedName: use-model-findbyid-to-search-your-database-by-id
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
MongoDB ではドキュメントが保存されるときに、フィールド `_id` が自動的に追加され、一意の英数字キーが設定されます。 `_id` での検索操作は非常に頻繁に行われるので、Mongoose では専用のメソッドが用意されています。
|
||||
|
||||
# --instructions--
|
||||
|
||||
`findPersonById` を変更し、`Model.findById() -> Person` を使用して、指定した `_id` を持つ人のみを検索してください。 検索キーとして関数引数 `personId` を使用してください。
|
||||
|
||||
# --hints--
|
||||
|
||||
Id でアイテムを正しく検索する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/find-by-id').then(
|
||||
(data) => {
|
||||
assert.equal(data.name, 'test', 'item.name is not what expected');
|
||||
assert.equal(data.age, 0, 'item.age is not what expected');
|
||||
assert.deepEqual(
|
||||
data.favoriteFoods,
|
||||
['none'],
|
||||
'item.favoriteFoods is not what expected'
|
||||
);
|
||||
assert.equal(data.__v, 0, 'The item should be not previously edited');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,51 @@
|
||||
---
|
||||
id: 587d7fb7367417b2b2512c0c
|
||||
title: Model.findOne() を使用して、一致する単一のドキュメントをデータベースから返す
|
||||
challengeType: 2
|
||||
forumTopicId: 301545
|
||||
dashedName: use-model-findone-to-return-a-single-matching-document-from-your-database
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
`Model.findOne()` の動作は `Model.find()` と似ていますが、アイテムが複数あっても (配列ではなく) 1 つのドキュメントだけを返します。 これは、プロパティを一意なものに宣言し、プロパティで検索をするときに特に便利です。
|
||||
|
||||
# --instructions--
|
||||
|
||||
`findOneByFood` 関数を変更し、`Model.findOne() -> Person` を使用して、好きな食べ物の中に特定の食べ物がある個人を 1 人検索してください。 検索キーとして関数引数 `food` を使用してください。
|
||||
|
||||
# --hints--
|
||||
|
||||
1 つのアイテムを正しく見つける必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.post(getUserInput('url') + '/_api/find-one-by-food', {
|
||||
name: 'Gary',
|
||||
age: 46,
|
||||
favoriteFoods: ['chicken salad']
|
||||
}).then(
|
||||
(data) => {
|
||||
assert.equal(data.name, 'Gary', 'item.name is not what expected');
|
||||
assert.deepEqual(
|
||||
data.favoriteFoods,
|
||||
['chicken salad'],
|
||||
'item.favoriteFoods is not what expected'
|
||||
);
|
||||
assert.equal(data.__v, 0, 'The item should be not previously edited');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
```
|
Reference in New Issue
Block a user