HTTPリクエストする場合の方法のメモ
- リクエストデータのチェック
- レスポンスデータのチェック
- get, post
あたりのみの簡易版
effect-ts
共通処理
共通処理コード
common.ts
import axios from 'axios'
import { Context, Data, Effect, Either, Layer, ManagedRuntime, Option } from 'effect'
export class ApiCallNg extends Data.TaggedError('API呼び出し失敗')<{ error: any }> {}
export class CheckRequestDataNg extends Data.TaggedError('リクエストデータチェックNG')<{
error: any
}> {}
export class CheckResponseDataNg extends Data.TaggedError('レスポンスデータ変換失敗')<{
error: any
}> {}
export class HttpRequestService extends Context.Tag('HttpRequestService')<
HttpRequestService,
{
readonly get: (url: string) => Effect.Effect<Either.Either<any, ApiCallNg>>
readonly post: (url: string, data: any) => Effect.Effect<Either.Either<any, ApiCallNg>>
}
>() {
static readonly Axios = Layer.succeed(HttpRequestService, {
get: function (url: string) {
return Effect.gen(function* () {
const effect = Effect.tryPromise({
try: () => axios.get(url).then((response) => response.data),
catch: (error) => new ApiCallNg({ error })
})
const either = yield* Effect.either(effect)
return either
})
},
post: function (url: string, request: any) {
return Effect.gen(function* () {
const effect = Effect.tryPromise({
try: () => axios.post(url, request).then((response) => response.data),
catch: (error) => new ApiCallNg({ error })
})
const either = yield* Effect.either(effect)
return either
})
}
})
}
export class UrlService extends Context.Tag('UrlService')<
UrlService,
{
readonly url: string
readonly requestData?: any
}
>() {}
export class RequestDataCheckService extends Context.Tag('RequestDataCheckService')<
RequestDataCheckService,
{
readonly checkRequestData: (data: any) => Either.Either<any, CheckRequestDataNg>
}
>() {}
export class ResponseDataCheckService extends Context.Tag('ResponseDataCheckService')<
ResponseDataCheckService,
{
readonly checkResponseData: (data: any) => Either.Either<any, CheckResponseDataNg>
}
>() {}
const getRuntime = (
httpService: Layer.Layer<HttpRequestService, never, never>,
urlService: Layer.Layer<UrlService, never, never>,
checkRequestService?: Layer.Layer<RequestDataCheckService, never, never>,
checkResponseService?: Layer.Layer<ResponseDataCheckService, never, never>
) => {
const array: any = [urlService, httpService]
if (checkRequestService) {
array.push(checkRequestService)
}
if (checkResponseService) {
array.push(checkResponseService)
}
const layers = Layer.mergeAll(...(array as any))
const runtime = ManagedRuntime.make(layers as any)
return runtime
}
const AxiosRuntime = (
urlService: Layer.Layer<UrlService, never, never>,
checkRequestService?: Layer.Layer<RequestDataCheckService, never, never>,
checkResponseService?: Layer.Layer<ResponseDataCheckService, never, never>
) => getRuntime(HttpRequestService.Axios, urlService, checkRequestService, checkResponseService)
function GetApi<A>() {
return Effect.gen(function* () {
const urlService = yield* UrlService
const httpService = yield* HttpRequestService
const checkService = yield* ResponseDataCheckService
const apiResult = yield* httpService.get(urlService.url)
const dataResult = Either.match(apiResult, {
onLeft: (left) => Either.left(left),
onRight: (data) =>
checkService.checkResponseData(data) as Either.Either<A, CheckResponseDataNg>
})
return dataResult as Either.Either<A, ApiCallNg | CheckResponseDataNg>
})
}
function PostApi<A>() {
return Effect.gen(function* () {
const urlService = yield* UrlService
const httpService = yield* HttpRequestService
const option_CheckRequestService = yield* Effect.serviceOption(RequestDataCheckService)
const option_CheckResponseService = yield* Effect.serviceOption(ResponseDataCheckService)
type ngType = ApiCallNg | CheckRequestDataNg | CheckResponseDataNg
const requestData = Option.isNone(option_CheckRequestService)
? undefined
: option_CheckRequestService.value.checkRequestData(urlService.requestData)
if (requestData !== undefined && Either.isLeft(requestData)) {
return requestData as Either.Either<A, ngType>
}
const apiResult = yield* httpService.post(urlService.url, requestData)
const dataResult = Either.match(apiResult, {
onLeft: (left) => Either.left(left),
onRight: (data: A) =>
Option.isNone(option_CheckResponseService)
? Either.right(data)
: option_CheckResponseService.value.checkResponseData(data)
})
return dataResult as Either.Either<A, ngType>
})
}
export default { GetApi, PostApi, AxiosRuntime, getRuntime }
getを使用する : api_123
api_123実装コード
api_123.ts
import { Either, Layer, Schema } from 'effect'
import { CheckResponseDataNg, ResponseDataCheckService, UrlService } from './common'
import common from './common'
const api_123 = Layer.succeed(UrlService, {
url: 'https://example.com/123'
})
const response_api_123_schema = Schema.Struct({
test1: Schema.String,
test2: Schema.Number
})
export type response_api_123_type = Schema.Schema.Type<typeof response_api_123_schema>
const check_response_api_123 = Layer.succeed(ResponseDataCheckService, {
checkResponseData: (data: any) => {
const checkResult = Schema.decodeEither(response_api_123_schema)(data)
const checkResultMapLeft = Either.mapLeft(
checkResult,
(error) => new CheckResponseDataNg({ error })
)
return checkResultMapLeft
}
})
export const call_api_123 = (
runtime = common.AxiosRuntime,
api = api_123,
check_request = undefined,
check_response = check_response_api_123
) => runtime(api, check_request, check_response).runPromise(common.GetApi<response_api_123_type>())
export default {
response_api_123_schema,
check_response_api_123
}
テストコード
import { Effect, Either, Layer } from 'effect'
import { describe, it } from 'vitest'
import {
ApiCallNg,
HttpRequestService,
RequestDataCheckService,
ResponseDataCheckService,
UrlService
} from '../common'
import common from '../common'
import { call_api_123, type response_api_123_type } from '../api_123'
// テストでのモック設定
function mock_post() {
return Effect.gen(function* () {
yield* Effect.log('mock post')
return Either.right(undefined)
})
}
describe('HTTP Request サンプル - api_123 - get', () => {
// モック設定
const TestUrlService = Layer.succeed(HttpRequestService, {
get: function (url: string) {
return Effect.gen(function* () {
yield* Effect.log('test get')
switch (url) {
case 'ApiCallNg': {
const error = new ApiCallNg({ error: 'test error' })
const response = Either.left(error)
return response
}
case 'ResponseDataNg': {
const testResponseData = {
test: 'ng response data'
}
const response = Either.right(testResponseData)
return response
}
case 'OK':
case 'https://example.com/123':
default: {
const testResponseData: response_api_123_type = {
test1: 'test string',
test2: 123
}
const response = Either.right(testResponseData)
return response
}
}
})
},
post: mock_post
})
const TestRuntime = (
urlService: Layer.Layer<UrlService, never, never>,
checkRequestService?: Layer.Layer<RequestDataCheckService, never, never>,
checkResponseService?: Layer.Layer<ResponseDataCheckService, never, never>
) => common.getRuntime(TestUrlService, urlService, checkRequestService, checkResponseService)
it('実際の呼び出し', async () => {
const result = await call_api_123()
console.log(result)
})
it('モックテスト時の呼び出し - ok', async () => {
const result = await call_api_123(TestRuntime)
console.log(result)
// OK処理サンプル
if (Either.isRight(result)) {
console.log('処理成功時の後続処理')
}
})
it('モックテスト時の呼び出し - ng - API呼び出し失敗', async () => {
const ng_url = Layer.succeed(UrlService, {
url: 'ApiCallNg'
})
const result = await call_api_123(TestRuntime, ng_url)
console.log(result)
// NG処理サンプル
if (Either.isLeft(result)) {
if (result.left._tag === 'API呼び出し失敗') {
console.log('API呼び出し失敗のリカバリ処理')
}
}
})
it('モックテスト時の呼び出し - ng - レスポンスデータ変換失敗', async () => {
const ng_url = Layer.succeed(UrlService, {
url: 'ResponseDataNg'
})
const result = await call_api_123(TestRuntime, ng_url)
console.log(result)
// NG処理サンプル
if (Either.isLeft(result)) {
if (result.left._tag === 'レスポンスデータ変換失敗') {
console.log('レスポンスデータ変換失敗のリカバリ処理')
}
}
})
})
実行結果
Error: Cross origin http://localhost:3000 forbidden
at dispatchError (C:\Users\masashi\Desktop\Vue\test\vue-project\node_modules\jsdom\lib\jsdom\living\xhr\xhr-utils.js:63:19)
at Object.validCORSHeaders (C:\Users\masashi\Desktop\Vue\test\vue-project\node_modules\jsdom\lib\jsdom\living\xhr\xhr-utils.js:75:5)
at receiveResponse (C:\Users\masashi\Desktop\Vue\test\vue-project\node_modules\jsdom\lib\jsdom\living\xhr\XMLHttpRequest-impl.js:794:19)
at Request.<anonymous> (C:\Users\masashi\Desktop\Vue\test\vue-project\node_modules\jsdom\lib\jsdom\living\xhr\XMLHttpRequest-impl.js:658:43)
at Request.emit (node:events:517:28)
at Request._processResponse (C:\Users\masashi\Desktop\Vue\test\vue-project\node_modules\jsdom\lib\jsdom\living\helpers\http-request.js:235:12)
at ClientRequest.<anonymous> (C:\Users\masashi\Desktop\Vue\test\vue-project\node_modules\jsdom\lib\jsdom\living\helpers\http-request.js:107:12)
at Object.onceWrapper (node:events:632:26)
at ClientRequest.emit (node:events:517:28)
at HTTPParser.parserOnIncomingClient [as onIncoming] (node:_http_client:700:27) undefined
{
_id: 'Either',
_tag: 'Left',
left: {
error: AxiosError: Network Error
at XMLHttpRequest.handleError (file:///C:/Users/masashi/Desktop/Vue/test/vue-project/node_modules/axios/lib/adapters/xhr.js:110:14)
at XMLHttpRequest.invokeTheCallbackFunction (C:\Users\masashi\Desktop\Vue\test\vue-project\node_modules\jsdom\lib\jsdom\living\generated\EventHandlerNonNull.js:14:28)
at XMLHttpRequest.<anonymous> (C:\Users\masashi\Desktop\Vue\test\vue-project\node_modules\jsdom\lib\jsdom\living\helpers\create-event-accessor.js:35:32)
at innerInvokeEventListeners (C:\Users\masashi\Desktop\Vue\test\vue-project\node_modules\jsdom\lib\jsdom\living\events\EventTarget-impl.js:350:25)
at invokeEventListeners (C:\Users\masashi\Desktop\Vue\test\vue-project\node_modules\jsdom\lib\jsdom\living\events\EventTarget-impl.js:286:3)
at XMLHttpRequestImpl._dispatch (C:\Users\masashi\Desktop\Vue\test\vue-project\node_modules\jsdom\lib\jsdom\living\events\EventTarget-impl.js:233:9)
at fireAnEvent (C:\Users\masashi\Desktop\Vue\test\vue-project\node_modules\jsdom\lib\jsdom\living\helpers\events.js:18:36)
at requestErrorSteps (C:\Users\masashi\Desktop\Vue\test\vue-project\node_modules\jsdom\lib\jsdom\living\xhr\xhr-utils.js:131:3)
at dispatchError (C:\Users\masashi\Desktop\Vue\test\vue-project\node_modules\jsdom\lib\jsdom\living\xhr\xhr-utils.js:60:3)
at Object.validCORSHeaders (C:\Users\masashi\Desktop\Vue\test\vue-project\node_modules\jsdom\lib\jsdom\living\xhr\xhr-utils.js:75:5)
at Axios.request (file:///C:/Users/masashi/Desktop/Vue/test/vue-project/node_modules/axios/lib/core/Axios.js:45:41)
at processTicksAndRejections (node:internal/process/task_queues:95:5) {
code: 'ERR_NETWORK',
config: [Object],
request: XMLHttpRequest {}
},
_tag: 'API呼び出し失敗'
}
}
timestamp=2025-03-20T06:15:09.627Z level=INFO fiber=#10 message="test get"
{
_id: 'Either',
_tag: 'Right',
right: { test1: 'test string', test2: 123 }
}
処理成功時の後続処理
timestamp=2025-03-20T06:15:09.638Z level=INFO fiber=#19 message="test get"
{
_id: 'Either',
_tag: 'Left',
left: { error: 'test error', _tag: 'API呼び出し失敗' }
}
API呼び出し失敗のリカバリ処理
timestamp=2025-03-20T06:15:09.646Z level=INFO fiber=#28 message="test get"
{
_id: 'Either',
_tag: 'Left',
left: {
error: {
_id: 'ParseError',
message: '{ readonly test1: string; readonly test2: number }\n' +
'└─ ["test1"]\n' +
' └─ is missing'
},
_tag: 'レスポンスデータ変換失敗'
}
}
レスポンスデータ変換失敗のリカバリ処理
postを使用する : api_456
api_456実装コード
api_456.ts
import { Layer, Schema } from 'effect'
import { UrlService } from './common'
import common from './common'
import api_123 from './api_123'
const api_456 = Layer.succeed(UrlService, {
url: 'https://example.com/456'
})
const response_api_456_schema = api_123.response_api_123_schema
export type response_api_456_type = Schema.Schema.Type<typeof response_api_456_schema>
const check_response_api_456 = api_123.check_response_api_123
export const call_api_456 = (
runtime = common.AxiosRuntime,
api = api_456,
check_request = undefined,
check_response = check_response_api_456
) => runtime(api, check_request, check_response).runPromise(common.PostApi<response_api_456_type>())
テストコード
import { Effect, Either, Layer } from 'effect'
import { describe, it } from 'vitest'
import {
ApiCallNg,
HttpRequestService,
RequestDataCheckService,
ResponseDataCheckService,
UrlService
} from '../common'
import common from '../common'
import { call_api_456, type response_api_456_type } from '../api_456'
// テストでのモック設定
function mock_get() {
return Effect.gen(function* () {
yield* Effect.log('mock get')
return Either.right(undefined)
})
}
describe('HTTP Request サンプル - api_456 - post', () => {
// モック設定
const TestUrlService = Layer.succeed(HttpRequestService, {
get: mock_get,
post: function (url: string) {
return Effect.gen(function* () {
yield* Effect.log('test post')
switch (url) {
case 'ApiCallNg': {
const error = new ApiCallNg({ error: 'test error' })
const response = Either.left(error)
return response
}
case 'ResponseDataNg': {
const testResponseData = {
test: 'ng response data'
}
const response = Either.right(testResponseData)
return response
}
case 'OK':
case 'https://example.com/456':
default: {
const testResponseData: response_api_456_type = {
test1: 'test string',
test2: 456
}
const response = Either.right(testResponseData)
return response
}
}
})
}
})
const TestRuntime = (
urlService: Layer.Layer<UrlService, never, never>,
checkRequestService?: Layer.Layer<RequestDataCheckService, never, never>,
checkResponseService?: Layer.Layer<ResponseDataCheckService, never, never>
) => common.getRuntime(TestUrlService, urlService, checkRequestService, checkResponseService)
it('実際の呼び出し', async () => {
const result = await call_api_456()
console.log(result)
})
it('モックテスト時の呼び出し - ok', async () => {
const result = await call_api_456(TestRuntime)
console.log(result)
// OK処理サンプル
if (Either.isRight(result)) {
console.log('処理成功時の後続処理')
}
})
it('モックテスト時の呼び出し - ng - API呼び出し失敗', async () => {
const ng_url = Layer.succeed(UrlService, {
url: 'ApiCallNg'
})
const result = await call_api_456(TestRuntime, ng_url)
console.log(result)
// NG処理サンプル
if (Either.isLeft(result)) {
if (result.left._tag === 'API呼び出し失敗') {
console.log('API呼び出し失敗のリカバリ処理')
}
}
})
it('モックテスト時の呼び出し - ng - レスポンスデータ変換失敗', async () => {
const ng_url = Layer.succeed(UrlService, {
url: 'ResponseDataNg'
})
const result = await call_api_456(TestRuntime, ng_url)
console.log(result)
// NG処理サンプル
if (Either.isLeft(result)) {
if (result.left._tag === 'レスポンスデータ変換失敗') {
console.log('レスポンスデータ変換失敗のリカバリ処理')
}
}
})
})
実行結果
Error: Cross origin http://localhost:3000 forbidden
at dispatchError (C:\Users\masashi\Desktop\Vue\test\vue-project\node_modules\jsdom\lib\jsdom\living\xhr\xhr-utils.js:63:19)
at Object.validCORSHeaders (C:\Users\masashi\Desktop\Vue\test\vue-project\node_modules\jsdom\lib\jsdom\living\xhr\xhr-utils.js:75:5)
at receiveResponse (C:\Users\masashi\Desktop\Vue\test\vue-project\node_modules\jsdom\lib\jsdom\living\xhr\XMLHttpRequest-impl.js:794:19)
at Request.<anonymous> (C:\Users\masashi\Desktop\Vue\test\vue-project\node_modules\jsdom\lib\jsdom\living\xhr\XMLHttpRequest-impl.js:658:43)
at Request.emit (node:events:517:28)
at Request._processResponse (C:\Users\masashi\Desktop\Vue\test\vue-project\node_modules\jsdom\lib\jsdom\living\helpers\http-request.js:235:12)
at ClientRequest.<anonymous> (C:\Users\masashi\Desktop\Vue\test\vue-project\node_modules\jsdom\lib\jsdom\living\helpers\http-request.js:107:12)
at Object.onceWrapper (node:events:632:26)
at ClientRequest.emit (node:events:517:28)
at HTTPParser.parserOnIncomingClient [as onIncoming] (node:_http_client:700:27) undefined
{
_id: 'Either',
_tag: 'Left',
left: {
error: AxiosError: Network Error
at XMLHttpRequest.handleError (file:///C:/Users/masashi/Desktop/Vue/test/vue-project/node_modules/axios/lib/adapters/xhr.js:110:14)
at XMLHttpRequest.invokeTheCallbackFunction (C:\Users\masashi\Desktop\Vue\test\vue-project\node_modules\jsdom\lib\jsdom\living\generated\EventHandlerNonNull.js:14:28)
at XMLHttpRequest.<anonymous> (C:\Users\masashi\Desktop\Vue\test\vue-project\node_modules\jsdom\lib\jsdom\living\helpers\create-event-accessor.js:35:32)
at innerInvokeEventListeners (C:\Users\masashi\Desktop\Vue\test\vue-project\node_modules\jsdom\lib\jsdom\living\events\EventTarget-impl.js:350:25)
at invokeEventListeners (C:\Users\masashi\Desktop\Vue\test\vue-project\node_modules\jsdom\lib\jsdom\living\events\EventTarget-impl.js:286:3)
at XMLHttpRequestImpl._dispatch (C:\Users\masashi\Desktop\Vue\test\vue-project\node_modules\jsdom\lib\jsdom\living\events\EventTarget-impl.js:233:9)
at fireAnEvent (C:\Users\masashi\Desktop\Vue\test\vue-project\node_modules\jsdom\lib\jsdom\living\helpers\events.js:18:36)
at requestErrorSteps (C:\Users\masashi\Desktop\Vue\test\vue-project\node_modules\jsdom\lib\jsdom\living\xhr\xhr-utils.js:131:3)
at dispatchError (C:\Users\masashi\Desktop\Vue\test\vue-project\node_modules\jsdom\lib\jsdom\living\xhr\xhr-utils.js:60:3)
at Object.validCORSHeaders (C:\Users\masashi\Desktop\Vue\test\vue-project\node_modules\jsdom\lib\jsdom\living\xhr\xhr-utils.js:75:5)
at Axios.request (file:///C:/Users/masashi/Desktop/Vue/test/vue-project/node_modules/axios/lib/core/Axios.js:45:41)
at processTicksAndRejections (node:internal/process/task_queues:95:5) {
code: 'ERR_NETWORK',
config: [Object],
request: XMLHttpRequest {}
},
_tag: 'API呼び出し失敗'
}
}
timestamp=2025-03-20T06:15:38.901Z level=INFO fiber=#10 message="test post"
{
_id: 'Either',
_tag: 'Right',
right: { test1: 'test string', test2: 456 }
}
処理成功時の後続処理
timestamp=2025-03-20T06:15:38.909Z level=INFO fiber=#19 message="test post"
{
_id: 'Either',
_tag: 'Left',
left: { error: 'test error', _tag: 'API呼び出し失敗' }
}
API呼び出し失敗のリカバリ処理
timestamp=2025-03-20T06:15:38.914Z level=INFO fiber=#28 message="test post"
{
_id: 'Either',
_tag: 'Left',
left: {
error: {
_id: 'ParseError',
message: '{ readonly test1: string; readonly test2: number }\n' +
'└─ ["test1"]\n' +
' └─ is missing'
},
_tag: 'レスポンスデータ変換失敗'
}
}
レスポンスデータ変換失敗のリカバリ処理
postとリクエストデータを使用する : api_789
api_789実装コード
api_789.ts
import { Either, Layer, Schema } from 'effect'
import { CheckRequestDataNg, RequestDataCheckService, UrlService } from './common'
import common from './common'
const api_789 = (requestData: request_api_789_type) =>
Layer.succeed(UrlService, {
url: 'https://example.com/789',
requestData
})
const request_api_789_schema = Schema.Struct({
name: Schema.String,
age: Schema.Number
})
export type request_api_789_type = Schema.Schema.Type<typeof request_api_789_schema>
const check_request_api_789 = Layer.succeed(RequestDataCheckService, {
checkRequestData: (data: any) => {
const checkResult = Schema.encodeEither(request_api_789_schema)(data)
const checkResultMapLeft = Either.mapLeft(
checkResult,
(error) => new CheckRequestDataNg({ error })
)
return checkResultMapLeft
}
})
const base_call_api_789 =
(requestData: request_api_789_type) =>
(
runtime = common.AxiosRuntime,
api = api_789(requestData),
check_request = check_request_api_789,
check_response = undefined
) =>
runtime(api, check_request, check_response).runPromise(common.PostApi())
export const call_api_789 = (requestData: request_api_789_type) => base_call_api_789(requestData)()
export default {
base_call_api_789
}
テストコード
import { Effect, Either, Layer } from 'effect'
import { describe, it } from 'vitest'
import {
ApiCallNg,
HttpRequestService,
RequestDataCheckService,
ResponseDataCheckService,
UrlService
} from '../common'
import common from '../common'
import { call_api_789, type request_api_789_type } from '../api_789'
import api_789 from '../api_789'
// テストでのモック設定
function mock_get() {
return Effect.gen(function* () {
yield* Effect.log('mock get')
return Either.right(undefined)
})
}
describe('HTTP Request サンプル - api_789 - post + data', () => {
// モック設定
const TestLayer = Layer.succeed(HttpRequestService, {
get: mock_get,
post: function (url: string) {
return Effect.gen(function* () {
yield* Effect.log('test post')
switch (url) {
case 'ApiCallNg': {
const error = new ApiCallNg({ error: 'test error' })
const response = Either.left(error)
return response
}
case 'OK':
case 'https://example.com/789':
default: {
const response = Either.right(undefined)
return response
}
}
})
}
})
const TestRuntime = (
urlService: Layer.Layer<UrlService, never, never>,
checkRequestService?: Layer.Layer<RequestDataCheckService, never, never>,
checkResponseService?: Layer.Layer<ResponseDataCheckService, never, never>
) => common.getRuntime(TestLayer, urlService, checkRequestService, checkResponseService)
it('実際の呼び出し', async () => {
const request: request_api_789_type = {
name: 'test name',
age: 789
}
const result = await call_api_789(request)
console.log(result)
})
it('モックテスト時の呼び出し - ok', async () => {
const request: request_api_789_type = {
name: 'test name',
age: 789
}
const result = await api_789.base_call_api_789(request)(TestRuntime)
console.log(result)
// OK処理サンプル
if (Either.isRight(result)) {
console.log('処理成功時の後続処理')
}
})
it('モックテスト時の呼び出し - ng - API呼び出し失敗', async () => {
const request: request_api_789_type = {
name: 'test name',
age: 789
}
const ng_url = Layer.succeed(UrlService, {
url: 'ApiCallNg',
requestData: request
})
const result = await api_789.base_call_api_789(request)(TestRuntime, ng_url)
console.log(result)
// NG処理サンプル
if (Either.isLeft(result)) {
if (result.left._tag === 'API呼び出し失敗') {
console.log('API呼び出し失敗のリカバリ処理')
}
}
})
it('モックテスト時の呼び出し - ng - リクエストデータチェックNG', async () => {
const request: any = {
test1: 'test name',
test2: 789
}
const result = await api_789.base_call_api_789(request)(TestRuntime)
console.log(result)
// NG処理サンプル
if (Either.isLeft(result)) {
if (result.left._tag === 'リクエストデータチェックNG') {
console.log('リクエストデータチェックNGのリカバリ処理')
}
}
})
})
実行結果
Error: Cross origin http://localhost:3000 forbidden
at dispatchError (C:\Users\masashi\Desktop\Vue\test\vue-project\node_modules\jsdom\lib\jsdom\living\xhr\xhr-utils.js:63:19)
at Object.validCORSHeaders (C:\Users\masashi\Desktop\Vue\test\vue-project\node_modules\jsdom\lib\jsdom\living\xhr\xhr-utils.js:75:5)
at receiveResponse (C:\Users\masashi\Desktop\Vue\test\vue-project\node_modules\jsdom\lib\jsdom\living\xhr\XMLHttpRequest-impl.js:794:19)
at Request.<anonymous> (C:\Users\masashi\Desktop\Vue\test\vue-project\node_modules\jsdom\lib\jsdom\living\xhr\XMLHttpRequest-impl.js:658:43)
at Request.emit (node:events:517:28)
at Request._processResponse (C:\Users\masashi\Desktop\Vue\test\vue-project\node_modules\jsdom\lib\jsdom\living\helpers\http-request.js:235:12)
at ClientRequest.<anonymous> (C:\Users\masashi\Desktop\Vue\test\vue-project\node_modules\jsdom\lib\jsdom\living\helpers\http-request.js:107:12)
at Object.onceWrapper (node:events:632:26)
at ClientRequest.emit (node:events:529:35)
at HTTPParser.parserOnIncomingClient [as onIncoming] (node:_http_client:700:27) undefined
{
_id: 'Either',
_tag: 'Left',
left: {
error: AxiosError: Network Error
at XMLHttpRequest.handleError (file:///C:/Users/masashi/Desktop/Vue/test/vue-project/node_modules/axios/lib/adapters/xhr.js:110:14)
at XMLHttpRequest.invokeTheCallbackFunction (C:\Users\masashi\Desktop\Vue\test\vue-project\node_modules\jsdom\lib\jsdom\living\generated\EventHandlerNonNull.js:14:28)
at XMLHttpRequest.<anonymous> (C:\Users\masashi\Desktop\Vue\test\vue-project\node_modules\jsdom\lib\jsdom\living\helpers\create-event-accessor.js:35:32)
at innerInvokeEventListeners (C:\Users\masashi\Desktop\Vue\test\vue-project\node_modules\jsdom\lib\jsdom\living\events\EventTarget-impl.js:350:25)
at invokeEventListeners (C:\Users\masashi\Desktop\Vue\test\vue-project\node_modules\jsdom\lib\jsdom\living\events\EventTarget-impl.js:286:3)
at XMLHttpRequestImpl._dispatch (C:\Users\masashi\Desktop\Vue\test\vue-project\node_modules\jsdom\lib\jsdom\living\events\EventTarget-impl.js:233:9)
at fireAnEvent (C:\Users\masashi\Desktop\Vue\test\vue-project\node_modules\jsdom\lib\jsdom\living\helpers\events.js:18:36)
at requestErrorSteps (C:\Users\masashi\Desktop\Vue\test\vue-project\node_modules\jsdom\lib\jsdom\living\xhr\xhr-utils.js:131:3)
at dispatchError (C:\Users\masashi\Desktop\Vue\test\vue-project\node_modules\jsdom\lib\jsdom\living\xhr\xhr-utils.js:60:3)
at Object.validCORSHeaders (C:\Users\masashi\Desktop\Vue\test\vue-project\node_modules\jsdom\lib\jsdom\living\xhr\xhr-utils.js:75:5)
at Axios.request (file:///C:/Users/masashi/Desktop/Vue/test/vue-project/node_modules/axios/lib/core/Axios.js:45:41)
at processTicksAndRejections (node:internal/process/task_queues:95:5) {
code: 'ERR_NETWORK',
config: [Object],
request: XMLHttpRequest {}
},
_tag: 'API呼び出し失敗'
}
}
timestamp=2025-03-20T06:18:54.784Z level=INFO fiber=#10 message="test post"
{ _id: 'Either', _tag: 'Right', right: undefined }
処理成功時の後続処理
timestamp=2025-03-20T06:18:54.793Z level=INFO fiber=#19 message="test post"
{
_id: 'Either',
_tag: 'Left',
left: { error: 'test error', _tag: 'API呼び出し失敗' }
}
API呼び出し失敗のリカバリ処理
{
_id: 'Either',
_tag: 'Left',
left: {
error: {
_id: 'ParseError',
message: '{ readonly name: string; readonly age: number }\n' +
'└─ ["name"]\n' +
' └─ is missing'
},
_tag: 'リクエストデータチェックNG'
}
}
リクエストデータチェックNGのリカバリ処理